eigent/backend/app/run_journal/store.py
2026-08-21 23:24:13 +08:00

18488 lines
712 KiB
Python

# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
# Licensed 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.
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
"""SQLite implementation of the Desktop-owned RunJournal.
The store owns one connection and serializes every transaction with a process
lock. SQLite still provides the durable cross-process writer lock; the local
lock makes the intended single-writer boundary explicit when async callers use
``asyncio.to_thread``.
"""
from __future__ import annotations
import hashlib
import json
import logging
import sqlite3
import threading
import time
import uuid
from collections.abc import Iterator
from contextlib import contextmanager
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
from app.run_journal.cloud_projection import cloud_event_payload
from app.run_journal.memory_policy import assert_memory_entry_policy
from app.run_journal.models import (
ApprovalRecord,
ApprovalRuleRecord,
ArtifactUploadSyncItem,
AttemptEnvironmentBinding,
CloudRunEventReplica,
CloudRunReplica,
CommandResultEvent,
CommandResultSyncBatch,
CommittedRunEvent,
ContextProjectionDiagnosticRecord,
ContinuationClaimRecord,
EffectiveEnvironmentSpecRecord,
FollowUpRequestRecord,
GitAgentWorkspaceRecord,
GitChangeSetItemRecord,
GitChangeSetRecord,
GitCheckpointRecord,
GitMutationIntentRecord,
GitOperationRecord,
GitRepositoryRecord,
HumanInteractionDecisionRecord,
HumanInteractionOptionRecord,
HumanInteractionRecord,
MemoryEntryRecord,
MemoryMutationRecord,
MemoryMutationResult,
MemoryMutationSyncBatch,
MemoryMutationSyncItem,
MemoryReconciliationRecord,
MemoryScopeStateRecord,
ModelInvocationEventRecord,
ModelInvocationRecord,
ProjectExecutionStateRecord,
ProjectGitStateRecord,
ProjectHistoryEventRecord,
ProjectWorkspaceBindingRecord,
RemoteCommandInboxRecord,
RunAttemptRecord,
RunEventDraft,
RunEventSyncBatch,
RunEventSyncOutboxRecord,
RunGitMaterializationRecord,
RunRecord,
SecurityAuditEventRecord,
SpacePermissionProfileRecord,
SpacePermissionProfileRevisionRecord,
StartupReconciliationResult,
ToolCallRecord,
WorkspaceBundleInstallProposalRecord,
WorkspaceBundleLocalBindingRecord,
WorkspaceBundleSecretBindingRecord,
WorkspaceConfigDraftAssetDescriptorRecord,
WorkspaceConfigDraftAssetRecord,
WorkspaceConfigDraftRecord,
WorkspaceConfigMaterializationRecord,
WorkspaceConfigRevisionRecord,
WorkspaceOverlayEntryRecord,
WorkspaceReadSnapshotRecord,
WorkspaceSnapshotRangeRecord,
WorkspaceWriterLeaseRecord,
WorkspaceWriterReleaseResult,
WorkspaceWriterRequestRecord,
)
from app.run_journal.paths import default_run_journal_path
from app.run_journal.transitions import (
ATTEMPT_ACTIVE_STATES,
ATTEMPT_TRANSITIONS,
COMMAND_TRANSITIONS,
RUN_TRANSITIONS,
TOOL_TRANSITIONS,
transition_allowed,
)
from app.run_policy import (
RunTimeoutPolicy,
TimeoutOutcome,
TimeoutScope,
ToolSafetyClass,
automatic_tool_replay_allowed,
)
from app.workspace_config.models import (
EffectiveEnvironmentSpec,
ThinkingEffort,
canonical_digest,
canonical_json,
)
SCHEMA_VERSION = 32
logger = logging.getLogger("run_journal")
# Per redacted request or response. Oversized documents retain a bounded JSON
# prefix plus the byte count and digest of the full redacted projection.
_MODEL_INVOCATION_DOCUMENT_MAX_BYTES = 1024 * 1024
def _redact_model_document(key: str, value: dict[str, Any]) -> dict[str, Any]:
# Import lazily: permission_policy.service depends on RunJournal, so an
# eager package import here would create a module-initialization cycle.
from app.permission_policy.models import redact_action_arguments
projected = redact_action_arguments({key: value})[key]
assert isinstance(projected, dict)
return projected
def _project_model_document(
key: str,
value: dict[str, Any],
) -> tuple[dict[str, Any], str]:
"""Redact and bound one model document before it reaches SQLite."""
projected = _redact_model_document(key, value)
encoded = canonical_json(projected)
encoded_bytes = encoded.encode("utf-8")
if len(encoded_bytes) <= _MODEL_INVOCATION_DOCUMENT_MAX_BYTES:
return projected, encoded
metadata = {
"truncated": True,
"kind": key,
"original_bytes": len(encoded_bytes),
"original_sha256": hashlib.sha256(encoded_bytes).hexdigest(),
}
low = 0
# A stored prefix cannot contain more characters than the byte budget.
# Keeping this upper bound independent of the source size prevents the
# budget calculation itself from copying very large candidate strings.
high = min(len(encoded), _MODEL_INVOCATION_DOCUMENT_MAX_BYTES)
bounded: dict[str, Any] = {}
bounded_json = ""
while low <= high:
midpoint = (low + high) // 2
candidate = {
"_eigent_capture": metadata,
"json_prefix": encoded[:midpoint],
}
candidate_json = canonical_json(candidate)
if (
len(candidate_json.encode("utf-8"))
<= _MODEL_INVOCATION_DOCUMENT_MAX_BYTES
):
bounded = candidate
bounded_json = candidate_json
low = midpoint + 1
else:
high = midpoint - 1
if not bounded_json:
raise ValueError("model invocation document budget is too small")
return bounded, bounded_json
_MEMORY_SCOPE_TYPES = {"project", "space", "user"}
_MEMORY_KINDS = {
"fact",
"decision",
"constraint",
"preference",
"todo",
"lesson",
}
_MEMORY_SOURCE_TRUST = {
"user_confirmed",
"user_asserted",
"system_verified",
"tool_observed",
"external_untrusted",
"model_inferred",
"legacy_unverified",
}
_MEMORY_DEFAULT_TOKEN_LIMITS = {"project": 1024, "space": 640, "user": 384}
_MIGRATION_V1 = """
BEGIN IMMEDIATE;
CREATE TABLE IF NOT EXISTS run_journal_migrations (
version INTEGER PRIMARY KEY,
applied_at REAL NOT NULL
);
CREATE TABLE IF NOT EXISTS runs (
run_id TEXT PRIMARY KEY,
project_id TEXT NOT NULL,
status TEXT NOT NULL CHECK (
status IN (
'pending', 'running', 'waiting_for_user', 'interrupted',
'completed', 'failed', 'cancelled'
)
),
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
active_attempt_id TEXT,
deadline_at REAL,
timeout_policy_version TEXT NOT NULL,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS runs_project_updated_idx
ON runs(project_id, updated_at DESC);
CREATE TABLE IF NOT EXISTS run_attempts (
attempt_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL REFERENCES runs(run_id) ON DELETE CASCADE,
attempt_number INTEGER NOT NULL CHECK (attempt_number > 0),
status TEXT NOT NULL,
started_at REAL NOT NULL,
ended_at REAL,
outcome TEXT,
timeout_reason TEXT,
UNIQUE(run_id, attempt_number)
);
CREATE TABLE IF NOT EXISTS run_events (
event_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL REFERENCES runs(run_id) ON DELETE RESTRICT,
sequence INTEGER NOT NULL CHECK (sequence > 0),
run_version INTEGER NOT NULL CHECK (run_version > 0),
event_type TEXT NOT NULL,
payload_json TEXT NOT NULL,
legacy_step TEXT,
created_at REAL NOT NULL,
UNIQUE(run_id, sequence)
);
CREATE INDEX IF NOT EXISTS run_events_replay_idx
ON run_events(run_id, sequence);
CREATE TABLE IF NOT EXISTS run_event_sync_outbox (
event_id TEXT PRIMARY KEY REFERENCES run_events(event_id) ON DELETE CASCADE,
run_id TEXT NOT NULL,
run_sequence INTEGER NOT NULL CHECK (run_sequence > 0),
status TEXT NOT NULL DEFAULT 'pending' CHECK (
status IN ('pending', 'sending', 'sent', 'dead_letter')
),
attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (attempt_count >= 0),
next_attempt_at REAL NOT NULL,
last_error TEXT,
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
UNIQUE(run_id, run_sequence)
);
CREATE INDEX IF NOT EXISTS run_event_sync_pending_idx
ON run_event_sync_outbox(status, next_attempt_at, run_id, run_sequence);
CREATE TABLE IF NOT EXISTS tool_calls (
tool_call_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL REFERENCES runs(run_id) ON DELETE CASCADE,
attempt_id TEXT REFERENCES run_attempts(attempt_id) ON DELETE SET NULL,
tool_name TEXT NOT NULL,
status TEXT NOT NULL,
safety_class TEXT NOT NULL,
idempotency_key TEXT,
outcome TEXT,
timeout_reason TEXT,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS tool_calls_run_idx
ON tool_calls(run_id, created_at);
CREATE TABLE IF NOT EXISTS approvals (
approval_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL REFERENCES runs(run_id) ON DELETE CASCADE,
attempt_id TEXT REFERENCES run_attempts(attempt_id) ON DELETE SET NULL,
status TEXT NOT NULL,
prompt_json TEXT NOT NULL,
decision_json TEXT,
created_at REAL NOT NULL,
resolved_at REAL
);
CREATE INDEX IF NOT EXISTS approvals_run_idx
ON approvals(run_id, created_at);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (1, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 1;
COMMIT;
"""
_MIGRATION_V2 = """
BEGIN IMMEDIATE;
ALTER TABLE run_event_sync_outbox ADD COLUMN lease_token TEXT;
ALTER TABLE run_event_sync_outbox ADD COLUMN lease_until REAL;
DROP INDEX IF EXISTS run_event_sync_pending_idx;
CREATE INDEX run_event_sync_pending_idx
ON run_event_sync_outbox(
status, next_attempt_at, lease_until, run_id, run_sequence
);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (2, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 2;
COMMIT;
"""
_MIGRATION_V3 = """
BEGIN IMMEDIATE;
CREATE TABLE remote_command_inbox (
command_id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
user_id INTEGER NOT NULL,
project_id TEXT NOT NULL,
run_id TEXT,
route_version INTEGER NOT NULL CHECK (route_version > 0),
command_type TEXT NOT NULL,
payload_json TEXT NOT NULL,
expires_at REAL NOT NULL,
receipt_grace_until REAL NOT NULL CHECK (receipt_grace_until >= expires_at),
requires_online_receipt_confirmation INTEGER NOT NULL DEFAULT 0 CHECK (
requires_online_receipt_confirmation IN (0, 1)
),
receipt_event_id TEXT NOT NULL UNIQUE,
receipt_status TEXT NOT NULL DEFAULT 'pending' CHECK (
receipt_status IN ('pending', 'confirmed', 'expired_late')
),
state TEXT NOT NULL DEFAULT 'received' CHECK (
state IN ('received', 'dispatched', 'accepted', 'rejected', 'completed', 'failed')
),
dispatch_attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (dispatch_attempt_count >= 0),
last_error TEXT,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE INDEX remote_command_inbox_dispatch_idx
ON remote_command_inbox(state, updated_at, command_id);
CREATE TABLE command_result_events (
event_id TEXT PRIMARY KEY,
command_id TEXT NOT NULL REFERENCES remote_command_inbox(command_id) ON DELETE CASCADE,
command_event_sequence INTEGER NOT NULL CHECK (command_event_sequence > 0),
event_type TEXT NOT NULL,
payload_json TEXT NOT NULL,
occurred_at REAL NOT NULL,
UNIQUE(command_id, command_event_sequence)
);
CREATE INDEX command_result_events_replay_idx
ON command_result_events(command_id, command_event_sequence);
CREATE TABLE command_result_outbox (
event_id TEXT PRIMARY KEY REFERENCES command_result_events(event_id) ON DELETE CASCADE,
command_id TEXT NOT NULL,
command_event_sequence INTEGER NOT NULL CHECK (command_event_sequence > 0),
status TEXT NOT NULL DEFAULT 'pending' CHECK (
status IN ('pending', 'sending', 'sent', 'dead_letter')
),
attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (attempt_count >= 0),
next_attempt_at REAL NOT NULL,
lease_token TEXT,
lease_until REAL,
last_error TEXT,
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
UNIQUE(command_id, command_event_sequence)
);
CREATE INDEX command_result_outbox_pending_idx
ON command_result_outbox(
status, next_attempt_at, lease_until, command_id, command_event_sequence
);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (3, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 3;
COMMIT;
"""
_MIGRATION_V4 = """
BEGIN IMMEDIATE;
ALTER TABLE runs ADD COLUMN parent_run_id TEXT REFERENCES runs(run_id);
ALTER TABLE runs ADD COLUMN timeout_policy_json TEXT NOT NULL DEFAULT '{}';
ALTER TABLE runs ADD COLUMN cancel_request_id TEXT;
ALTER TABLE runs ADD COLUMN cancel_requested_at REAL;
ALTER TABLE run_attempts ADD COLUMN resume_request_id TEXT;
ALTER TABLE run_attempts ADD COLUMN resume_reason TEXT NOT NULL DEFAULT 'initial';
ALTER TABLE run_attempts ADD COLUMN policy_version TEXT NOT NULL DEFAULT 'v1';
ALTER TABLE run_attempts ADD COLUMN elapsed_active_ms INTEGER NOT NULL DEFAULT 0;
ALTER TABLE run_attempts ADD COLUMN last_consumer_heartbeat_at REAL;
CREATE UNIQUE INDEX run_attempts_resume_request_idx
ON run_attempts(run_id, resume_request_id)
WHERE resume_request_id IS NOT NULL;
ALTER TABLE tool_calls ADD COLUMN request_json TEXT NOT NULL DEFAULT '{}';
ALTER TABLE tool_calls ADD COLUMN result_json TEXT;
ALTER TABLE tool_calls ADD COLUMN prepared_at REAL;
ALTER TABLE tool_calls ADD COLUMN dispatched_at REAL;
ALTER TABLE tool_calls ADD COLUMN completed_at REAL;
ALTER TABLE approvals ADD COLUMN version INTEGER NOT NULL DEFAULT 0;
ALTER TABLE approvals ADD COLUMN expires_at REAL;
ALTER TABLE approvals ADD COLUMN expiry_action TEXT NOT NULL DEFAULT 'keep_pending';
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (4, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 4;
COMMIT;
"""
_MIGRATION_V5 = """
BEGIN IMMEDIATE;
ALTER TABLE runs ADD COLUMN origin TEXT NOT NULL DEFAULT 'local' CHECK (
origin IN ('local', 'cloud_restore')
);
ALTER TABLE runs ADD COLUMN resume_blocked_reason TEXT;
CREATE TABLE cloud_project_replicas (
project_id TEXT PRIMARY KEY,
last_cursor INTEGER NOT NULL DEFAULT 0 CHECK (last_cursor >= 0),
last_synced_at REAL NOT NULL
);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (5, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 5;
COMMIT;
"""
_MIGRATION_V6 = """
BEGIN IMMEDIATE;
CREATE TABLE workspace_config_revisions (
revision_id TEXT PRIMARY KEY,
bundle_id TEXT NOT NULL,
revision_number INTEGER NOT NULL CHECK (revision_number > 0),
status TEXT NOT NULL CHECK (
status IN ('draft', 'validated', 'published', 'deprecated')
),
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
manifest_json TEXT NOT NULL,
manifest_digest TEXT NOT NULL CHECK (length(manifest_digest) = 64),
created_by TEXT NOT NULL,
created_at REAL NOT NULL,
UNIQUE(bundle_id, revision_number)
);
CREATE INDEX workspace_config_revisions_bundle_idx
ON workspace_config_revisions(bundle_id, revision_number DESC);
CREATE TABLE workspace_config_materializations (
materialization_id TEXT PRIMARY KEY,
space_id TEXT NOT NULL,
revision_id TEXT NOT NULL REFERENCES workspace_config_revisions(
revision_id
) ON DELETE RESTRICT,
config_placement TEXT NOT NULL CHECK (
config_placement IN ('in_repo', 'sidecar')
),
state TEXT NOT NULL CHECK (
state IN ('pending', 'materialized', 'needs_attention', 'degraded')
),
local_override_digest TEXT NOT NULL DEFAULT '',
materialized_at REAL,
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
UNIQUE(space_id, revision_id, local_override_digest)
);
CREATE TABLE effective_environment_specs (
environment_spec_id TEXT PRIMARY KEY,
owner_type TEXT NOT NULL CHECK (owner_type IN ('run', 'run_attempt')),
owner_id TEXT NOT NULL,
bundle_revision_id TEXT NOT NULL REFERENCES workspace_config_revisions(
revision_id
) ON DELETE RESTRICT,
manifest_digest TEXT NOT NULL CHECK (length(manifest_digest) = 64),
spec_json TEXT NOT NULL,
environment_spec_digest TEXT NOT NULL CHECK (
length(environment_spec_digest) = 64
),
semantic_spec_digest TEXT NOT NULL CHECK (
length(semantic_spec_digest) = 64
),
local_materialization_digest TEXT NOT NULL CHECK (
length(local_materialization_digest) = 64
),
redacted_spec_json TEXT NOT NULL,
projection_digest TEXT NOT NULL CHECK (length(projection_digest) = 64),
permission_profile_revision TEXT NOT NULL,
provider_capability_revision TEXT NOT NULL,
created_at REAL NOT NULL
);
CREATE INDEX effective_environment_specs_owner_idx
ON effective_environment_specs(owner_type, owner_id, created_at DESC);
ALTER TABLE run_attempts ADD COLUMN environment_spec_id TEXT REFERENCES
effective_environment_specs(environment_spec_id) ON DELETE RESTRICT;
ALTER TABLE run_attempts ADD COLUMN environment_spec_digest TEXT;
ALTER TABLE run_attempts ADD COLUMN bundle_revision_id TEXT REFERENCES
workspace_config_revisions(revision_id) ON DELETE RESTRICT;
ALTER TABLE run_attempts ADD COLUMN permission_profile_revision TEXT;
ALTER TABLE run_attempts ADD COLUMN thinking_effort_requested TEXT CHECK (
thinking_effort_requested IS NULL OR
thinking_effort_requested IN ('low', 'medium', 'high', 'xhigh', 'max')
);
ALTER TABLE run_attempts ADD COLUMN thinking_effort_effective TEXT CHECK (
thinking_effort_effective IS NULL OR
thinking_effort_effective IN ('low', 'medium', 'high', 'xhigh', 'max')
);
ALTER TABLE run_attempts ADD COLUMN provider_capability_revision TEXT;
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (6, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 6;
COMMIT;
"""
_MIGRATION_V7 = """
BEGIN IMMEDIATE;
CREATE TABLE git_repositories (
repository_id TEXT PRIMARY KEY,
space_id TEXT NOT NULL,
repository_role TEXT NOT NULL CHECK (
repository_role IN ('content', 'configuration')
),
root_path TEXT NOT NULL,
root_path_digest TEXT NOT NULL CHECK (length(root_path_digest) = 64),
ownership TEXT NOT NULL CHECK (
ownership IN ('eigent_owned', 'adopted')
),
state TEXT NOT NULL CHECK (
state IN ('ready', 'not_enabled', 'needs_attention', 'degraded')
),
version_coverage TEXT NOT NULL CHECK (
version_coverage IN ('full', 'managed_files_only', 'degraded')
),
hooks_mode TEXT NOT NULL DEFAULT 'disabled' CHECK (
hooks_mode IN ('disabled', 'trusted')
),
repo_subdir TEXT,
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
UNIQUE(space_id, repository_role)
);
CREATE TABLE git_operations (
operation_id TEXT PRIMARY KEY,
repository_id TEXT NOT NULL REFERENCES git_repositories(
repository_id
) ON DELETE RESTRICT,
request_id TEXT NOT NULL,
operation_type TEXT NOT NULL,
payload_digest TEXT NOT NULL CHECK (length(payload_digest) = 64),
status TEXT NOT NULL CHECK (
status IN (
'prepared', 'dispatched', 'completed', 'failed',
'outcome_unknown'
)
),
expected_repo_state_digest TEXT,
observed_repo_state_digest TEXT,
result_json TEXT,
error_code TEXT,
error_message TEXT,
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
UNIQUE(repository_id, request_id)
);
CREATE INDEX git_operations_reconcile_idx
ON git_operations(status, updated_at, repository_id);
CREATE TABLE git_checkpoints (
checkpoint_id TEXT PRIMARY KEY,
repository_id TEXT NOT NULL REFERENCES git_repositories(
repository_id
) ON DELETE RESTRICT,
operation_id TEXT NOT NULL UNIQUE REFERENCES git_operations(
operation_id
) ON DELETE RESTRICT,
target_role TEXT NOT NULL CHECK (
target_role IN ('user', 'project', 'run', 'agent')
),
target_id TEXT NOT NULL,
commit_oid TEXT NOT NULL,
parent_oid TEXT,
paths_json TEXT NOT NULL,
actor_id TEXT NOT NULL,
trigger TEXT NOT NULL,
message TEXT NOT NULL,
created_at REAL NOT NULL
);
CREATE INDEX git_checkpoints_repository_created_idx
ON git_checkpoints(repository_id, created_at DESC);
CREATE TABLE git_managed_paths (
repository_id TEXT NOT NULL REFERENCES git_repositories(
repository_id
) ON DELETE CASCADE,
relative_path TEXT NOT NULL,
source TEXT NOT NULL CHECK (
source IN (
'agent_created', 'agent_modified', 'user_selected',
'configuration', 'overlay_preimage'
)
),
first_checkpoint_id TEXT REFERENCES git_checkpoints(
checkpoint_id
) ON DELETE SET NULL,
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
PRIMARY KEY(repository_id, relative_path)
);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (7, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 7;
COMMIT;
"""
_MIGRATION_V8 = """
BEGIN IMMEDIATE;
CREATE TABLE git_project_integrations (
project_id TEXT PRIMARY KEY,
repository_id TEXT NOT NULL REFERENCES git_repositories(
repository_id
) ON DELETE RESTRICT,
integration_ref TEXT,
integration_head TEXT,
last_synced_user_head TEXT,
pending_apply INTEGER NOT NULL DEFAULT 0 CHECK (
pending_apply IN (0, 1)
),
worktree_path TEXT,
projected_head TEXT,
state TEXT NOT NULL DEFAULT 'unmaterialized' CHECK (
state IN (
'unmaterialized', 'ready', 'needs_attention', 'conflicted',
'archived'
)
),
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE INDEX git_project_integrations_repository_idx
ON git_project_integrations(repository_id, updated_at DESC);
CREATE TABLE git_run_materializations (
run_id TEXT PRIMARY KEY REFERENCES runs(run_id) ON DELETE CASCADE,
project_id TEXT NOT NULL,
repository_id TEXT NOT NULL REFERENCES git_repositories(
repository_id
) ON DELETE RESTRICT,
workspace_base_ref TEXT,
workspace_base_commit TEXT,
project_state_version INTEGER NOT NULL CHECK (
project_state_version >= 0
),
materialization_state TEXT NOT NULL DEFAULT 'unmaterialized' CHECK (
materialization_state IN (
'unmaterialized', 'materializing', 'materialized', 'promoted',
'conflicted', 'needs_attention', 'archived'
)
),
run_ref TEXT,
worktree_path TEXT,
promoted_commit TEXT,
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
FOREIGN KEY(project_id) REFERENCES git_project_integrations(project_id)
ON DELETE RESTRICT
);
CREATE INDEX git_run_materializations_project_idx
ON git_run_materializations(project_id, created_at DESC);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (8, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 8;
COMMIT;
"""
_MIGRATION_V9 = """
BEGIN IMMEDIATE;
CREATE TABLE workspace_read_snapshots (
snapshot_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL REFERENCES runs(run_id) ON DELETE CASCADE,
project_id TEXT NOT NULL,
repository_id TEXT NOT NULL REFERENCES git_repositories(
repository_id
) ON DELETE RESTRICT,
generation INTEGER NOT NULL CHECK (generation >= 0),
project_base_commit TEXT,
common_base_commit TEXT,
project_state_version INTEGER NOT NULL CHECK (
project_state_version >= 0
),
snapshot_ref TEXT,
user_head TEXT,
user_working_state_digest TEXT NOT NULL CHECK (
length(user_working_state_digest) = 64
),
overlay_manifest_digest TEXT NOT NULL CHECK (
length(overlay_manifest_digest) = 64
),
state TEXT NOT NULL DEFAULT 'active' CHECK (
state IN ('active', 'stale', 'unavailable', 'released')
),
expires_at REAL,
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
UNIQUE(run_id, generation)
);
CREATE INDEX workspace_read_snapshots_run_idx
ON workspace_read_snapshots(run_id, generation DESC);
CREATE TABLE workspace_overlay_entries (
snapshot_id TEXT NOT NULL REFERENCES workspace_read_snapshots(
snapshot_id
) ON DELETE CASCADE,
relative_path TEXT NOT NULL,
source_kind TEXT NOT NULL CHECK (
source_kind IN ('project_blob', 'user_overlay', 'missing')
),
entry_state TEXT NOT NULL CHECK (
entry_state IN (
'read_only', 'imported_preimage', 'agent_modified', 'conflicted'
)
),
source_token_json TEXT NOT NULL,
project_blob_oid TEXT,
size_bytes INTEGER NOT NULL CHECK (size_bytes >= 0),
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
PRIMARY KEY(snapshot_id, relative_path)
);
CREATE TABLE workspace_snapshot_ranges (
snapshot_id TEXT NOT NULL,
relative_path TEXT NOT NULL,
start_offset INTEGER NOT NULL CHECK (start_offset >= 0),
end_offset INTEGER NOT NULL CHECK (end_offset >= start_offset),
content_digest TEXT NOT NULL CHECK (length(content_digest) = 64),
cache_key TEXT NOT NULL CHECK (length(cache_key) = 64),
created_at REAL NOT NULL,
PRIMARY KEY(snapshot_id, relative_path, start_offset, end_offset),
FOREIGN KEY(snapshot_id, relative_path) REFERENCES workspace_overlay_entries(
snapshot_id, relative_path
) ON DELETE CASCADE
);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (9, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 9;
COMMIT;
"""
_MIGRATION_V10 = """
BEGIN IMMEDIATE;
ALTER TABLE workspace_overlay_entries
ADD COLUMN materialized_content_digest TEXT;
ALTER TABLE workspace_overlay_entries
ADD COLUMN preimage_cache_key TEXT;
CREATE TABLE git_change_sets (
change_set_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL REFERENCES runs(run_id) ON DELETE CASCADE,
repository_id TEXT NOT NULL REFERENCES git_repositories(
repository_id
) ON DELETE RESTRICT,
worktree_ref TEXT NOT NULL,
base_commit TEXT,
state TEXT NOT NULL DEFAULT 'open' CHECK (
state IN ('open', 'checkpointed', 'discarded', 'needs_attention')
),
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
UNIQUE(run_id, worktree_ref)
);
CREATE INDEX git_change_sets_run_idx
ON git_change_sets(run_id, created_at DESC);
CREATE TABLE git_change_set_items (
change_set_id TEXT NOT NULL REFERENCES git_change_sets(
change_set_id
) ON DELETE CASCADE,
relative_path TEXT NOT NULL,
operation_request_id TEXT NOT NULL,
actor_id TEXT NOT NULL,
trigger TEXT NOT NULL,
change_kind TEXT NOT NULL CHECK (
change_kind IN ('added', 'modified', 'deleted', 'renamed')
),
source TEXT NOT NULL CHECK (
source IN (
'agent_created', 'agent_modified', 'user_selected',
'overlay_preimage', 'artifact_event', 'worktree_delta'
)
),
preimage_digest TEXT,
result_digest TEXT,
size_bytes INTEGER CHECK (size_bytes IS NULL OR size_bytes >= 0),
item_state TEXT NOT NULL DEFAULT 'pending' CHECK (
item_state IN (
'pending', 'preimage_checkpointed', 'checkpointed', 'ignored'
)
),
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
PRIMARY KEY(change_set_id, relative_path)
);
CREATE TABLE git_mutation_intents (
intent_id TEXT PRIMARY KEY,
change_set_id TEXT NOT NULL REFERENCES git_change_sets(
change_set_id
) ON DELETE CASCADE,
operation_request_id TEXT NOT NULL,
mutation_scope TEXT NOT NULL CHECK (
mutation_scope IN ('exact_path', 'broad_process')
),
relative_path TEXT,
preimage_digest TEXT,
actor_id TEXT NOT NULL,
trigger TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'prepared' CHECK (
status IN ('prepared', 'completed', 'needs_attention')
),
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
UNIQUE(change_set_id, operation_request_id),
CHECK (
(mutation_scope = 'exact_path' AND relative_path IS NOT NULL)
OR (mutation_scope = 'broad_process' AND relative_path IS NULL)
)
);
CREATE INDEX git_mutation_intents_reconcile_idx
ON git_mutation_intents(status, updated_at, change_set_id);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (10, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 10;
COMMIT;
"""
_MIGRATION_V11 = """
BEGIN IMMEDIATE;
CREATE TABLE human_interactions (
interaction_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL REFERENCES runs(run_id) ON DELETE CASCADE,
attempt_id TEXT REFERENCES run_attempts(attempt_id) ON DELETE SET NULL,
interaction_type TEXT NOT NULL CHECK (
interaction_type IN (
'question', 'choice', 'form', 'confirmation', 'approval',
'diff_review', 'merge_conflict', 'credential_binding'
)
),
status TEXT NOT NULL CHECK (
status IN ('requested', 'presented', 'resolved', 'expired', 'cancelled')
),
request_json TEXT NOT NULL,
response_schema_json TEXT NOT NULL DEFAULT '{}',
requested_by TEXT NOT NULL,
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
expires_at REAL,
presented_at REAL,
resolved_at REAL,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE INDEX human_interactions_run_status_idx
ON human_interactions(run_id, status, created_at);
CREATE TABLE human_interaction_options (
interaction_id TEXT NOT NULL REFERENCES human_interactions(interaction_id)
ON DELETE CASCADE,
option_id TEXT NOT NULL,
position INTEGER NOT NULL CHECK (position >= 0),
label TEXT NOT NULL,
value_json TEXT NOT NULL,
description TEXT,
PRIMARY KEY(interaction_id, option_id),
UNIQUE(interaction_id, position)
);
CREATE TABLE human_interaction_decisions (
decision_id TEXT PRIMARY KEY,
interaction_id TEXT NOT NULL REFERENCES human_interactions(interaction_id)
ON DELETE CASCADE,
decision_request_id TEXT NOT NULL,
decision_json TEXT NOT NULL,
actor_type TEXT NOT NULL CHECK (
actor_type IN ('user', 'auto_reviewer', 'system')
),
actor_id TEXT,
source TEXT NOT NULL CHECK (
source IN ('desktop', 'remote_control', 'recovery', 'expiry')
),
action_digest TEXT,
created_at REAL NOT NULL,
UNIQUE(interaction_id, decision_request_id)
);
CREATE INDEX human_interaction_decisions_interaction_idx
ON human_interaction_decisions(interaction_id, created_at);
ALTER TABLE approvals ADD COLUMN action_digest TEXT NOT NULL DEFAULT '';
ALTER TABLE approvals ADD COLUMN policy_revision TEXT NOT NULL DEFAULT 'legacy';
ALTER TABLE approvals ADD COLUMN safety_class TEXT NOT NULL DEFAULT 'unknown';
ALTER TABLE approvals ADD COLUMN decision_scope TEXT NOT NULL DEFAULT 'once';
INSERT INTO human_interactions(
interaction_id, run_id, attempt_id, interaction_type, status,
request_json, response_schema_json, requested_by, version, expires_at,
presented_at, resolved_at, created_at, updated_at
)
SELECT approval_id, run_id, attempt_id, 'approval',
CASE WHEN status = 'pending' THEN 'requested' ELSE 'resolved' END,
prompt_json, '{}', 'legacy_approval', version, expires_at, NULL,
resolved_at, created_at, COALESCE(resolved_at, created_at)
FROM approvals;
CREATE TABLE space_permission_profiles (
space_id TEXT PRIMARY KEY,
profile_name TEXT NOT NULL CHECK (
profile_name IN (
'read_only', 'request_approval', 'auto_reviewer', 'full_access'
)
),
sandbox_mode TEXT NOT NULL,
approval_mode TEXT NOT NULL,
reviewer_mode TEXT NOT NULL,
revision INTEGER NOT NULL DEFAULT 1 CHECK (revision > 0),
updated_by TEXT NOT NULL,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE TABLE approval_rules (
rule_id TEXT PRIMARY KEY,
space_id TEXT NOT NULL,
effect TEXT NOT NULL CHECK (effect IN ('allow', 'prompt', 'deny')),
action_pattern TEXT NOT NULL,
resource_pattern TEXT,
scope TEXT NOT NULL CHECK (scope IN ('run', 'space')),
run_id TEXT REFERENCES runs(run_id) ON DELETE CASCADE,
source_interaction_id TEXT REFERENCES human_interactions(interaction_id)
ON DELETE SET NULL,
expires_at REAL,
created_by TEXT NOT NULL,
created_at REAL NOT NULL,
CHECK ((scope = 'run' AND run_id IS NOT NULL) OR scope = 'space')
);
CREATE INDEX approval_rules_lookup_idx
ON approval_rules(space_id, action_pattern, effect, expires_at);
CREATE TABLE security_audit_events (
audit_event_id TEXT PRIMARY KEY,
space_id TEXT,
run_id TEXT REFERENCES runs(run_id) ON DELETE SET NULL,
interaction_id TEXT REFERENCES human_interactions(interaction_id)
ON DELETE SET NULL,
event_type TEXT NOT NULL,
actor_type TEXT NOT NULL,
actor_id TEXT,
action_digest TEXT,
details_json TEXT NOT NULL DEFAULT '{}',
created_at REAL NOT NULL
);
CREATE INDEX security_audit_events_run_idx
ON security_audit_events(run_id, created_at);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (11, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 11;
COMMIT;
"""
_MIGRATION_V12 = """
BEGIN IMMEDIATE;
CREATE TABLE space_permission_profile_revisions (
revision_id TEXT PRIMARY KEY,
space_id TEXT NOT NULL,
profile_name TEXT NOT NULL CHECK (
profile_name IN (
'read_only', 'request_approval', 'auto_reviewer', 'full_access'
)
),
sandbox_mode TEXT NOT NULL,
approval_mode TEXT NOT NULL,
reviewer_mode TEXT NOT NULL,
revision INTEGER NOT NULL CHECK (revision > 0),
created_by TEXT NOT NULL,
created_at REAL NOT NULL,
UNIQUE(space_id, revision)
);
INSERT INTO space_permission_profile_revisions(
revision_id, space_id, profile_name, sandbox_mode, approval_mode,
reviewer_mode, revision, created_by, created_at
)
SELECT 'space:' || space_id || ':' || revision, space_id, profile_name,
sandbox_mode, approval_mode, reviewer_mode, revision, updated_by,
updated_at
FROM space_permission_profiles;
CREATE INDEX space_permission_profile_revisions_space_idx
ON space_permission_profile_revisions(space_id, revision);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (12, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 12;
COMMIT;
"""
_MIGRATION_V13 = """
BEGIN IMMEDIATE;
CREATE TABLE git_agent_workspaces (
workspace_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL REFERENCES git_run_materializations(run_id)
ON DELETE CASCADE,
repository_id TEXT NOT NULL REFERENCES git_repositories(repository_id)
ON DELETE RESTRICT,
agent_id TEXT NOT NULL,
agent_ref TEXT NOT NULL UNIQUE,
worktree_path TEXT NOT NULL UNIQUE,
base_commit TEXT NOT NULL,
head_commit TEXT NOT NULL,
state TEXT NOT NULL CHECK (
state IN (
'admitted', 'materializing', 'ready', 'merging', 'merged',
'conflicted', 'needs_attention', 'archived'
)
),
lease_owner TEXT,
lease_token TEXT,
lease_until REAL,
last_operation_id TEXT,
conflict_interaction_id TEXT REFERENCES human_interactions(interaction_id)
ON DELETE SET NULL,
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
UNIQUE(run_id, agent_id),
CHECK (
(lease_owner IS NULL AND lease_token IS NULL AND lease_until IS NULL)
OR (lease_owner IS NOT NULL AND lease_token IS NOT NULL
AND lease_until IS NOT NULL)
)
);
CREATE INDEX git_agent_workspaces_reconcile_idx
ON git_agent_workspaces(state, lease_until, updated_at);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (13, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 13;
COMMIT;
"""
_MIGRATION_V14 = """
BEGIN IMMEDIATE;
CREATE TABLE workspace_bundle_install_proposals (
proposal_id TEXT PRIMARY KEY,
request_id TEXT NOT NULL UNIQUE,
space_id TEXT NOT NULL,
bundle_id TEXT NOT NULL,
revision_id TEXT NOT NULL,
config_placement TEXT NOT NULL CHECK (
config_placement IN ('in_repo', 'sidecar')
),
state TEXT NOT NULL CHECK (
state IN (
'proposed', 'approved', 'materializing', 'materialized',
'rejected', 'needs_attention'
)
),
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
manifest_json TEXT NOT NULL,
manifest_digest TEXT NOT NULL CHECK (length(manifest_digest) = 64),
assets_json TEXT NOT NULL,
install_plan_json TEXT NOT NULL,
decided_by TEXT,
decided_at REAL,
error_code TEXT,
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
CHECK (
(decided_by IS NULL AND decided_at IS NULL)
OR (decided_by IS NOT NULL AND decided_at IS NOT NULL)
)
);
CREATE INDEX workspace_bundle_install_proposals_space_idx
ON workspace_bundle_install_proposals(space_id, updated_at DESC);
CREATE TABLE workspace_bundle_local_bindings (
binding_id TEXT PRIMARY KEY,
proposal_id TEXT NOT NULL REFERENCES workspace_bundle_install_proposals(
proposal_id
) ON DELETE CASCADE,
slot_id TEXT NOT NULL,
binding_kind TEXT NOT NULL CHECK (
binding_kind IN ('connector', 'local_path', 'script_approval')
),
connector_id TEXT,
opaque_connection_id TEXT,
local_path TEXT,
required_grants_json TEXT NOT NULL,
authorized_by TEXT NOT NULL,
authorized_at REAL NOT NULL,
UNIQUE(proposal_id, slot_id)
);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (14, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 14;
COMMIT;
"""
_MIGRATION_V15 = """
BEGIN IMMEDIATE;
-- Legacy tool approvals could wait forever after their owning attempt was
-- interrupted. Give every unresolved approval a finite recovery horizon and
-- make expiry fail closed. The paired HumanInteraction uses the same deadline
-- so Desktop and Remote Control project one lifecycle.
UPDATE approvals
SET expires_at = created_at + 86400,
expiry_action = 'reject'
WHERE status = 'pending'
AND expires_at IS NULL;
UPDATE human_interactions
SET expires_at = COALESCE(
(
SELECT approvals.expires_at
FROM approvals
WHERE approvals.approval_id = human_interactions.interaction_id
),
created_at + 86400
),
updated_at = MAX(updated_at, created_at)
WHERE interaction_type = 'approval'
AND status IN ('requested', 'presented')
AND expires_at IS NULL;
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (15, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 15;
COMMIT;
"""
_MIGRATION_V16 = """
BEGIN IMMEDIATE;
CREATE TABLE IF NOT EXISTS workspace_config_drafts (
space_id TEXT PRIMARY KEY,
version INTEGER NOT NULL DEFAULT 1 CHECK (version >= 1),
base_revision_id TEXT REFERENCES workspace_config_revisions(
revision_id
) ON DELETE RESTRICT,
document_json TEXT NOT NULL,
document_digest TEXT NOT NULL CHECK (length(document_digest) = 64),
updated_by TEXT NOT NULL,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (16, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 16;
COMMIT;
"""
_MIGRATION_V17 = """
BEGIN IMMEDIATE;
CREATE TABLE IF NOT EXISTS workspace_bundle_secret_bindings (
binding_id TEXT PRIMARY KEY,
proposal_id TEXT NOT NULL REFERENCES workspace_bundle_install_proposals(
proposal_id
) ON DELETE CASCADE,
requirement_key TEXT NOT NULL,
requirement_kind TEXT NOT NULL CHECK (
requirement_kind IN ('environment', 'mcp_secret')
),
binding_version INTEGER NOT NULL DEFAULT 1 CHECK (binding_version >= 1),
secret_ref TEXT NOT NULL,
account_scope_digest TEXT NOT NULL CHECK (
length(account_scope_digest) = 64
),
authorized_by TEXT NOT NULL,
authorized_at REAL NOT NULL,
updated_at REAL NOT NULL,
UNIQUE(proposal_id, requirement_key)
);
CREATE TABLE IF NOT EXISTS workspace_bundle_secret_binding_requests (
client_request_id TEXT PRIMARY KEY,
proposal_id TEXT NOT NULL REFERENCES workspace_bundle_install_proposals(
proposal_id
) ON DELETE CASCADE,
request_digest TEXT NOT NULL CHECK (length(request_digest) = 64),
created_at REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS workspace_bundle_secret_bindings_proposal_idx
ON workspace_bundle_secret_bindings(proposal_id, requirement_kind);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (17, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 17;
COMMIT;
"""
_MIGRATION_V18 = """
BEGIN IMMEDIATE;
CREATE TABLE IF NOT EXISTS workspace_config_draft_asset_blobs (
content_digest TEXT PRIMARY KEY CHECK (length(content_digest) = 64),
content BLOB NOT NULL,
size_bytes INTEGER NOT NULL CHECK (size_bytes >= 0),
created_at REAL NOT NULL
);
CREATE TABLE IF NOT EXISTS workspace_config_draft_assets (
space_id TEXT NOT NULL,
draft_version INTEGER NOT NULL CHECK (draft_version >= 1),
document_digest TEXT NOT NULL CHECK (length(document_digest) = 64),
logical_path TEXT NOT NULL,
content_digest TEXT NOT NULL REFERENCES workspace_config_draft_asset_blobs(
content_digest
) ON DELETE RESTRICT,
media_type TEXT NOT NULL,
size_bytes INTEGER NOT NULL CHECK (size_bytes >= 0),
executable INTEGER NOT NULL CHECK (executable IN (0, 1)),
provenance TEXT NOT NULL,
created_at REAL NOT NULL,
PRIMARY KEY(space_id, draft_version, logical_path)
);
CREATE INDEX IF NOT EXISTS workspace_config_draft_assets_digest_idx
ON workspace_config_draft_assets(content_digest);
CREATE TABLE IF NOT EXISTS workspace_agent_plugin_import_requests (
client_request_id TEXT PRIMARY KEY,
space_id TEXT NOT NULL,
expected_target_draft_version INTEGER NOT NULL CHECK (
expected_target_draft_version >= 0
),
expected_review_digest TEXT NOT NULL CHECK (
length(expected_review_digest) = 64
),
requested_by TEXT NOT NULL,
request_digest TEXT NOT NULL CHECK (length(request_digest) = 64),
result_version INTEGER NOT NULL CHECK (result_version >= 1),
result_base_revision_id TEXT,
result_document_json TEXT NOT NULL,
result_document_digest TEXT NOT NULL CHECK (
length(result_document_digest) = 64
),
result_updated_by TEXT NOT NULL,
result_created_at REAL NOT NULL,
result_updated_at REAL NOT NULL,
created_at REAL NOT NULL
);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (18, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 18;
COMMIT;
"""
_MIGRATION_V19 = """
BEGIN IMMEDIATE;
CREATE UNIQUE INDEX IF NOT EXISTS run_events_one_assistant_final_idx
ON run_events(run_id)
WHERE event_type = 'assistant.final';
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (19, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 19;
COMMIT;
"""
_MIGRATION_V20 = """
BEGIN IMMEDIATE;
CREATE TABLE IF NOT EXISTS follow_up_requests(
request_id TEXT PRIMARY KEY,
project_id TEXT NOT NULL,
content TEXT NOT NULL CHECK(length(trim(content)) > 0),
attachment_paths_json TEXT NOT NULL DEFAULT '[]',
delivery_mode TEXT NOT NULL DEFAULT 'wait'
CHECK(delivery_mode IN ('wait', 'send_now')),
status TEXT NOT NULL DEFAULT 'pending'
CHECK(status IN ('pending', 'admitted', 'cancelled')),
admitted_run_id TEXT REFERENCES runs(run_id) ON DELETE SET NULL,
last_error TEXT,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS follow_up_requests_pending_idx
ON follow_up_requests(project_id, delivery_mode DESC, created_at)
WHERE status = 'pending';
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (20, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 20;
COMMIT;
"""
_MIGRATION_V21 = """
BEGIN IMMEDIATE;
-- The execution lease is the cross-writer enforcement point for the
-- one-executing-Run-per-Project invariant. A pending historical Run row is
-- not an execution owner; creating an Attempt is what acquires the lease.
CREATE TABLE IF NOT EXISTS project_run_execution_leases(
project_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL UNIQUE REFERENCES runs(run_id) ON DELETE CASCADE,
attempt_id TEXT NOT NULL UNIQUE REFERENCES run_attempts(attempt_id)
ON DELETE CASCADE,
acquired_at REAL NOT NULL
);
-- Existing databases may contain more than one active Attempt for a Project
-- because v20 had no cross-Run constraint. Keep the newest Attempt as owner;
-- startup reconciliation will interrupt the remaining orphaned Attempts.
INSERT OR IGNORE INTO project_run_execution_leases(
project_id, run_id, attempt_id, acquired_at
)
SELECT runs.project_id, attempts.run_id, attempts.attempt_id,
attempts.started_at
FROM run_attempts AS attempts
JOIN runs ON runs.run_id = attempts.run_id
WHERE attempts.status IN ('pending', 'running', 'waiting_for_user')
AND NOT EXISTS (
SELECT 1
FROM run_attempts AS newer
JOIN runs AS newer_run ON newer_run.run_id = newer.run_id
WHERE newer_run.project_id = runs.project_id
AND newer.status IN ('pending', 'running', 'waiting_for_user')
AND (
newer.started_at > attempts.started_at
OR (newer.started_at = attempts.started_at
AND newer.attempt_id > attempts.attempt_id)
)
);
ALTER TABLE follow_up_requests
ADD COLUMN source TEXT NOT NULL DEFAULT 'local'
CHECK(source IN ('local', 'remote_control', 'scheduled'));
ALTER TABLE follow_up_requests ADD COLUMN source_command_id TEXT;
CREATE UNIQUE INDEX IF NOT EXISTS follow_up_requests_source_command_idx
ON follow_up_requests(source_command_id)
WHERE source_command_id IS NOT NULL;
CREATE TABLE IF NOT EXISTS project_execution_states(
project_id TEXT PRIMARY KEY,
state_version INTEGER NOT NULL DEFAULT 0 CHECK(state_version >= 0),
frontier_json TEXT,
frontier_digest TEXT CHECK(
frontier_digest IS NULL OR length(frontier_digest) = 64
),
frontier_run_id TEXT REFERENCES runs(run_id) ON DELETE SET NULL,
updated_at REAL NOT NULL
);
CREATE TABLE IF NOT EXISTS continuation_claims(
fingerprint TEXT PRIMARY KEY CHECK(length(fingerprint) = 64),
request_id TEXT NOT NULL UNIQUE,
project_id TEXT NOT NULL,
project_state_version INTEGER NOT NULL CHECK(project_state_version >= 0),
intent TEXT NOT NULL,
base_run_id TEXT REFERENCES runs(run_id) ON DELETE SET NULL,
next_action TEXT,
created_at REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS continuation_claims_project_idx
ON continuation_claims(project_id, created_at DESC);
CREATE TABLE IF NOT EXISTS context_projection_diagnostics(
projection_id TEXT PRIMARY KEY,
project_id TEXT NOT NULL,
run_id TEXT NOT NULL REFERENCES runs(run_id) ON DELETE CASCADE,
source_event_ids_json TEXT NOT NULL,
source_memory_ids_json TEXT NOT NULL,
project_state_version INTEGER NOT NULL CHECK(project_state_version >= 0),
projection_digest TEXT NOT NULL CHECK(length(projection_digest) = 64),
token_count INTEGER NOT NULL CHECK(token_count >= 0),
created_at REAL NOT NULL,
UNIQUE(run_id, projection_digest)
);
CREATE INDEX IF NOT EXISTS context_projection_diagnostics_project_idx
ON context_projection_diagnostics(project_id, created_at DESC);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (21, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 21;
COMMIT;
"""
_MIGRATION_V22 = """
BEGIN IMMEDIATE;
-- History owns the durable, Project-wide order. Run sequence remains the
-- causal order inside one Run; journal_cursor is only for cross-Run paging.
CREATE TABLE IF NOT EXISTS project_history_cursors(
project_id TEXT PRIMARY KEY,
next_cursor INTEGER NOT NULL DEFAULT 1 CHECK(next_cursor >= 1),
updated_at REAL NOT NULL
);
CREATE TABLE IF NOT EXISTS project_history_events(
project_id TEXT NOT NULL,
journal_cursor INTEGER NOT NULL CHECK(journal_cursor >= 1),
event_id TEXT NOT NULL UNIQUE REFERENCES run_events(event_id)
ON DELETE CASCADE,
source_kind TEXT NOT NULL DEFAULT 'native' CHECK(
source_kind IN ('native', 'legacy_cursor_backfill')
),
created_at REAL NOT NULL,
PRIMARY KEY(project_id, journal_cursor)
);
CREATE INDEX IF NOT EXISTS project_history_events_page_idx
ON project_history_events(project_id, journal_cursor);
-- Deterministic compatibility order for canonical events created before the
-- Project cursor existed. It promises stable paging, not real-time order
-- between concurrent historical Runs.
INSERT OR IGNORE INTO project_history_events(
project_id, journal_cursor, event_id, source_kind, created_at
)
SELECT project_id, project_cursor, event_id,
'legacy_cursor_backfill', created_at
FROM (
SELECT runs.project_id AS project_id,
ROW_NUMBER() OVER (
PARTITION BY runs.project_id
ORDER BY run_events.created_at, run_events.run_id,
run_events.sequence, run_events.event_id
) AS project_cursor,
run_events.event_id AS event_id,
run_events.created_at AS created_at
FROM run_events
JOIN runs ON runs.run_id = run_events.run_id
);
INSERT OR REPLACE INTO project_history_cursors(
project_id, next_cursor, updated_at
)
SELECT project_id, MAX(journal_cursor) + 1, MAX(created_at)
FROM project_history_events
GROUP BY project_id;
CREATE TABLE IF NOT EXISTS memory_scope_state(
scope_type TEXT NOT NULL CHECK(scope_type IN ('project', 'space', 'user')),
scope_id TEXT NOT NULL,
owner_kind TEXT NOT NULL CHECK(owner_kind IN ('desktop', 'cloud')),
revision INTEGER NOT NULL DEFAULT 0 CHECK(revision >= 0),
capture_enabled INTEGER NOT NULL DEFAULT 1 CHECK(capture_enabled IN (0, 1)),
use_enabled INTEGER NOT NULL DEFAULT 1 CHECK(use_enabled IN (0, 1)),
sync_scope TEXT NOT NULL DEFAULT 'local_only' CHECK(
sync_scope IN ('local_only', 'metadata_only', 'summary_only', 'full_memory')
),
token_limit INTEGER NOT NULL CHECK(token_limit > 0),
current_token_count INTEGER NOT NULL DEFAULT 0 CHECK(current_token_count >= 0),
consolidate_threshold REAL NOT NULL DEFAULT 0.75 CHECK(
consolidate_threshold > 0 AND consolidate_threshold <= 1
),
processed_through_watermark TEXT,
watermark_kind TEXT CHECK(
watermark_kind IS NULL OR watermark_kind IN ('journal_cursor', 'cloud_cursor')
),
extractor_version TEXT NOT NULL DEFAULT 'memory-v2',
last_consolidated_at REAL,
last_error TEXT,
updated_at REAL NOT NULL,
PRIMARY KEY(scope_type, scope_id)
);
CREATE TABLE IF NOT EXISTS memory_entries(
memory_id TEXT PRIMARY KEY,
scope_type TEXT NOT NULL CHECK(scope_type IN ('project', 'space', 'user')),
scope_id TEXT NOT NULL,
kind TEXT NOT NULL CHECK(
kind IN ('fact', 'decision', 'constraint', 'preference', 'todo', 'lesson')
),
content TEXT NOT NULL CHECK(length(trim(content)) > 0),
priority TEXT NOT NULL DEFAULT 'normal' CHECK(priority IN ('normal', 'high')),
version INTEGER NOT NULL DEFAULT 1 CHECK(version >= 1),
token_count INTEGER NOT NULL CHECK(token_count > 0),
pinned_by_user INTEGER NOT NULL DEFAULT 0 CHECK(pinned_by_user IN (0, 1)),
confirmed_by_user INTEGER NOT NULL DEFAULT 0 CHECK(confirmed_by_user IN (0, 1)),
created_by TEXT NOT NULL CHECK(
created_by IN ('extractor', 'agent', 'user', 'importer')
),
source_trust TEXT NOT NULL CHECK(
source_trust IN (
'user_confirmed', 'user_asserted', 'system_verified',
'tool_observed', 'external_untrusted', 'model_inferred',
'legacy_unverified'
)
),
sensitivity TEXT NOT NULL DEFAULT 'normal' CHECK(
sensitivity IN ('normal', 'personal', 'sensitive')
),
source_refs_json TEXT NOT NULL DEFAULT '[]',
last_used_at REAL,
usage_count INTEGER NOT NULL DEFAULT 0 CHECK(usage_count >= 0),
deleted_at REAL,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS memory_entries_scope_active_idx
ON memory_entries(scope_type, scope_id, deleted_at, pinned_by_user DESC,
priority DESC, updated_at DESC);
CREATE TABLE IF NOT EXISTS memory_mutations(
mutation_id TEXT PRIMARY KEY,
memory_id TEXT,
scope_type TEXT NOT NULL CHECK(scope_type IN ('project', 'space', 'user')),
scope_id TEXT NOT NULL,
operation TEXT NOT NULL CHECK(
operation IN ('add', 'replace', 'remove', 'restore', 'confirm',
'pin', 'consolidate', 'noop')
),
expected_version INTEGER,
before_hash TEXT,
after_hash TEXT,
actor_type TEXT NOT NULL CHECK(
actor_type IN ('extractor', 'agent', 'user', 'importer', 'system')
),
actor_id TEXT,
run_id TEXT,
activity_id TEXT,
reason TEXT NOT NULL,
source_refs_json TEXT NOT NULL DEFAULT '[]',
idempotency_key TEXT NOT NULL UNIQUE,
request_digest TEXT NOT NULL CHECK(length(request_digest) = 64),
decision_id TEXT,
created_at REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS memory_mutations_scope_idx
ON memory_mutations(scope_type, scope_id, created_at DESC);
CREATE INDEX IF NOT EXISTS memory_mutations_entry_idx
ON memory_mutations(memory_id, created_at DESC);
CREATE TABLE IF NOT EXISTS memory_mutation_outbox(
mutation_id TEXT PRIMARY KEY REFERENCES memory_mutations(mutation_id)
ON DELETE CASCADE,
scope_type TEXT NOT NULL,
scope_id TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending' CHECK(
status IN ('pending', 'sending', 'sent', 'dead_letter')
),
attempt_count INTEGER NOT NULL DEFAULT 0 CHECK(attempt_count >= 0),
next_attempt_at REAL NOT NULL,
lease_token TEXT,
lease_until REAL,
last_error TEXT,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS memory_mutation_outbox_pending_idx
ON memory_mutation_outbox(status, next_attempt_at, lease_until,
scope_type, scope_id, created_at);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (22, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 22;
COMMIT;
"""
_MIGRATION_V23 = """
BEGIN IMMEDIATE;
-- The independent Memory lane must retain the exact post-mutation projection.
-- Reading the current entry at drain time would make an older mutation appear
-- to contain a later version after multiple offline edits.
ALTER TABLE memory_mutation_outbox
ADD COLUMN payload_json TEXT NOT NULL DEFAULT '{}';
-- revision is the canonical per-scope order. Timestamps and UUIDs are not a
-- safe FIFO when multiple mutations commit inside the same clock tick.
ALTER TABLE memory_mutation_outbox
ADD COLUMN scope_revision INTEGER NOT NULL DEFAULT 0;
-- Memory Sync is a product-level invariant rather than a user preference.
UPDATE memory_scope_state SET sync_scope = 'full_memory';
CREATE INDEX IF NOT EXISTS memory_mutation_outbox_scope_revision_idx
ON memory_mutation_outbox(scope_type, scope_id, status, scope_revision);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (23, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 23;
COMMIT;
"""
_MIGRATION_V24 = """
BEGIN IMMEDIATE;
CREATE TABLE IF NOT EXISTS legacy_memory_import_batches(
source_path TEXT NOT NULL,
source_checksum TEXT NOT NULL,
status TEXT NOT NULL CHECK(status IN ('completed', 'degraded')),
imported_count INTEGER NOT NULL DEFAULT 0 CHECK(imported_count >= 0),
skipped_count INTEGER NOT NULL DEFAULT 0 CHECK(skipped_count >= 0),
last_error TEXT,
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
PRIMARY KEY(source_path, source_checksum)
);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (24, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 24;
COMMIT;
"""
_MIGRATION_V25 = """
BEGIN IMMEDIATE;
-- Memory content sync is mandatory. Keep the invariant at the storage
-- boundary as well as in the API so no old caller can silently opt a scope
-- out and strand its independent mutation lane.
CREATE TRIGGER IF NOT EXISTS memory_scope_state_full_sync_insert
BEFORE INSERT ON memory_scope_state
WHEN NEW.sync_scope != 'full_memory'
BEGIN
SELECT RAISE(ABORT, 'Memory sync is fixed to full_memory');
END;
CREATE TRIGGER IF NOT EXISTS memory_scope_state_full_sync_update
BEFORE UPDATE OF sync_scope ON memory_scope_state
WHEN NEW.sync_scope != 'full_memory'
BEGIN
SELECT RAISE(ABORT, 'Memory sync is fixed to full_memory');
END;
-- Automatic extraction is Project-scoped in V2. Space/User Memory remains
-- editable and injectable, but cannot pretend to scan multiple Project
-- History streams with one cursor.
UPDATE memory_scope_state
SET capture_enabled = 0
WHERE scope_type IN ('space', 'user');
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (25, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 25;
COMMIT;
"""
_MIGRATION_V26 = """
BEGIN IMMEDIATE;
-- Artifact bytes use an independent durable lane. The canonical manifest and
-- this upload intent are committed together; successful upload later appends
-- artifact.uploaded and clears the lease in one local transaction.
CREATE TABLE IF NOT EXISTS artifact_upload_outbox(
artifact_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL REFERENCES runs(run_id) ON DELETE CASCADE,
project_id TEXT NOT NULL,
local_path TEXT NOT NULL,
filename TEXT NOT NULL,
relative_path TEXT NOT NULL,
file_size INTEGER NOT NULL CHECK(file_size >= 0),
status TEXT NOT NULL DEFAULT 'pending' CHECK(
status IN ('pending', 'sending', 'sent', 'dead_letter')
),
attempt_count INTEGER NOT NULL DEFAULT 0 CHECK(attempt_count >= 0),
next_attempt_at REAL NOT NULL,
lease_token TEXT,
lease_until REAL,
last_error TEXT,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS artifact_upload_outbox_pending_idx
ON artifact_upload_outbox(status, next_attempt_at, lease_until, created_at);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (26, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 26;
COMMIT;
"""
_MIGRATION_V27 = """
BEGIN IMMEDIATE;
-- A Desktop database can outlive login sessions. Memory scopes are bound to
-- the authenticated account that created/used the owning Space or Project;
-- unbound legacy/development rows never enter a later account's sync lane.
CREATE TABLE IF NOT EXISTS memory_scope_owners(
scope_type TEXT NOT NULL CHECK(scope_type IN ('project', 'space', 'user')),
scope_id TEXT NOT NULL,
account_owner_id TEXT NOT NULL CHECK(length(trim(account_owner_id)) > 0),
bound_at REAL NOT NULL,
PRIMARY KEY(scope_type, scope_id)
);
CREATE INDEX IF NOT EXISTS memory_scope_owners_account_idx
ON memory_scope_owners(account_owner_id, scope_type, scope_id);
-- Request payload identity is only a candidate. CloudSync promotes it after
-- the device-registration response proves the authenticated Cloud account.
CREATE TABLE IF NOT EXISTS memory_scope_owner_candidates(
scope_type TEXT NOT NULL CHECK(scope_type IN ('project', 'space', 'user')),
scope_id TEXT NOT NULL,
claimed_account_owner_id TEXT NOT NULL CHECK(
length(trim(claimed_account_owner_id)) > 0
),
created_at REAL NOT NULL,
PRIMARY KEY(scope_type, scope_id, claimed_account_owner_id)
);
CREATE INDEX IF NOT EXISTS memory_scope_owner_candidates_claim_idx
ON memory_scope_owner_candidates(
claimed_account_owner_id, scope_type, scope_id
);
-- Writer takeover conflicts are review facts, never silent last-write-wins.
CREATE TABLE IF NOT EXISTS memory_reconciliation_items(
reconciliation_id TEXT PRIMARY KEY,
account_owner_id TEXT NOT NULL,
scope_type TEXT NOT NULL,
scope_id TEXT NOT NULL,
memory_id TEXT NOT NULL,
local_entry_json TEXT NOT NULL,
cloud_entry_json TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending' CHECK(
status IN ('pending', 'accepted_local', 'accepted_cloud', 'dismissed')
),
created_at REAL NOT NULL,
resolved_at REAL,
UNIQUE(account_owner_id, scope_type, scope_id, memory_id, status)
);
CREATE INDEX IF NOT EXISTS memory_reconciliation_pending_idx
ON memory_reconciliation_items(account_owner_id, status, created_at);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (27, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 27;
COMMIT;
"""
_MIGRATION_V28 = """
BEGIN IMMEDIATE;
-- Persist the Cloud delivery fence across a Desktop restart between Inbox
-- commit and receipt confirmation.
ALTER TABLE remote_command_inbox ADD COLUMN delivery_lease_token TEXT;
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (28, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 28;
COMMIT;
"""
_MIGRATION_V29 = """
PRAGMA foreign_keys = OFF;
BEGIN IMMEDIATE;
-- Memory adoption is a durable review interaction. Earlier schemas knew
-- only the original doc-18 interaction types, so creating the review failed
-- after the user had already approved it. Rebuild the parent table without
-- renaming it first so the child-table foreign keys continue to target the
-- canonical name when the replacement is installed.
CREATE TABLE human_interactions_v29 (
interaction_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL REFERENCES runs(run_id) ON DELETE CASCADE,
attempt_id TEXT REFERENCES run_attempts(attempt_id) ON DELETE SET NULL,
interaction_type TEXT NOT NULL CHECK (
interaction_type IN (
'question', 'choice', 'form', 'confirmation', 'approval',
'diff_review', 'merge_conflict', 'credential_binding',
'memory_change_review'
)
),
status TEXT NOT NULL CHECK (
status IN ('requested', 'presented', 'resolved', 'expired', 'cancelled')
),
request_json TEXT NOT NULL,
response_schema_json TEXT NOT NULL DEFAULT '{}',
requested_by TEXT NOT NULL,
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
expires_at REAL,
presented_at REAL,
resolved_at REAL,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
INSERT INTO human_interactions_v29(
interaction_id, run_id, attempt_id, interaction_type, status,
request_json, response_schema_json, requested_by, version, expires_at,
presented_at, resolved_at, created_at, updated_at
)
SELECT interaction_id, run_id, attempt_id, interaction_type, status,
request_json, response_schema_json, requested_by, version, expires_at,
presented_at, resolved_at, created_at, updated_at
FROM human_interactions;
DROP TABLE human_interactions;
ALTER TABLE human_interactions_v29 RENAME TO human_interactions;
CREATE INDEX human_interactions_run_status_idx
ON human_interactions(run_id, status, created_at);
-- Cloud tombstones deliberately carry no plaintext. The original v22
-- content CHECK made a freshly restored tombstone impossible to persist and
-- permanently wedged writer takeover. Active Memory remains non-empty.
ALTER TABLE memory_entries RENAME TO memory_entries_v28;
CREATE TABLE memory_entries(
memory_id TEXT PRIMARY KEY,
scope_type TEXT NOT NULL CHECK(scope_type IN ('project', 'space', 'user')),
scope_id TEXT NOT NULL,
kind TEXT NOT NULL CHECK(
kind IN ('fact', 'decision', 'constraint', 'preference', 'todo', 'lesson')
),
content TEXT NOT NULL CHECK(
length(trim(content)) > 0 OR deleted_at IS NOT NULL
),
priority TEXT NOT NULL DEFAULT 'normal' CHECK(priority IN ('normal', 'high')),
version INTEGER NOT NULL DEFAULT 1 CHECK(version >= 1),
token_count INTEGER NOT NULL CHECK(token_count > 0),
pinned_by_user INTEGER NOT NULL DEFAULT 0 CHECK(pinned_by_user IN (0, 1)),
confirmed_by_user INTEGER NOT NULL DEFAULT 0 CHECK(confirmed_by_user IN (0, 1)),
created_by TEXT NOT NULL CHECK(
created_by IN ('extractor', 'agent', 'user', 'importer')
),
source_trust TEXT NOT NULL CHECK(
source_trust IN (
'user_confirmed', 'user_asserted', 'system_verified',
'tool_observed', 'external_untrusted', 'model_inferred',
'legacy_unverified'
)
),
sensitivity TEXT NOT NULL DEFAULT 'normal' CHECK(
sensitivity IN ('normal', 'personal', 'sensitive')
),
source_refs_json TEXT NOT NULL DEFAULT '[]',
last_used_at REAL,
usage_count INTEGER NOT NULL DEFAULT 0 CHECK(usage_count >= 0),
deleted_at REAL,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
INSERT INTO memory_entries(
memory_id, scope_type, scope_id, kind, content, priority, version,
token_count, pinned_by_user, confirmed_by_user, created_by, source_trust,
sensitivity, source_refs_json, last_used_at, usage_count, deleted_at,
created_at, updated_at
)
SELECT memory_id, scope_type, scope_id, kind, content, priority, version,
token_count, pinned_by_user, confirmed_by_user, created_by,
source_trust, sensitivity, source_refs_json, last_used_at, usage_count,
deleted_at, created_at, updated_at
FROM memory_entries_v28;
DROP TABLE memory_entries_v28;
CREATE INDEX memory_entries_scope_active_idx
ON memory_entries(scope_type, scope_id, deleted_at, pinned_by_user DESC,
priority DESC, updated_at DESC);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (29, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 29;
COMMIT;
PRAGMA foreign_keys = ON;
"""
_MIGRATION_V30 = """
BEGIN IMMEDIATE;
-- Model requests and responses are structured Run facts. Legacy camel_log
-- JSON files are projections of these rows, never the authoritative source.
CREATE TABLE IF NOT EXISTS model_invocations(
invocation_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL REFERENCES runs(run_id) ON DELETE CASCADE,
attempt_id TEXT REFERENCES run_attempts(attempt_id) ON DELETE SET NULL,
agent_id TEXT NOT NULL,
logical_call_id TEXT NOT NULL,
retry_index INTEGER NOT NULL CHECK(retry_index >= 0),
status TEXT NOT NULL CHECK(
status IN ('dispatched', 'completed', 'failed', 'outcome_unknown')
),
provider TEXT NOT NULL,
model TEXT NOT NULL,
transport TEXT NOT NULL,
thinking_effort TEXT,
request_json TEXT NOT NULL,
response_json TEXT,
request_digest TEXT NOT NULL CHECK(length(request_digest) = 64),
response_digest TEXT CHECK(
response_digest IS NULL OR length(response_digest) = 64
),
prompt_tokens INTEGER CHECK(prompt_tokens IS NULL OR prompt_tokens >= 0),
completion_tokens INTEGER CHECK(
completion_tokens IS NULL OR completion_tokens >= 0
),
cache_read_tokens INTEGER CHECK(
cache_read_tokens IS NULL OR cache_read_tokens >= 0
),
cache_write_tokens INTEGER CHECK(
cache_write_tokens IS NULL OR cache_write_tokens >= 0
),
finish_reason TEXT,
error_code TEXT,
error_message TEXT,
redaction_version TEXT NOT NULL,
started_at REAL NOT NULL,
first_token_at REAL,
completed_at REAL,
UNIQUE(logical_call_id, retry_index)
);
CREATE INDEX IF NOT EXISTS model_invocations_run_idx
ON model_invocations(run_id, started_at, invocation_id);
CREATE INDEX IF NOT EXISTS model_invocations_attempt_idx
ON model_invocations(attempt_id, started_at)
WHERE attempt_id IS NOT NULL;
CREATE TABLE IF NOT EXISTS model_invocation_events(
event_id TEXT PRIMARY KEY,
invocation_id TEXT NOT NULL REFERENCES model_invocations(invocation_id)
ON DELETE CASCADE,
event_index INTEGER NOT NULL CHECK(event_index > 0),
event_type TEXT NOT NULL CHECK(
event_type IN (
'dispatched', 'first_token', 'completed', 'failed',
'outcome_unknown'
)
),
payload_json TEXT NOT NULL,
created_at REAL NOT NULL,
UNIQUE(invocation_id, event_index)
);
CREATE INDEX IF NOT EXISTS model_invocation_events_timeline_idx
ON model_invocation_events(invocation_id, event_index);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (30, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 30;
COMMIT;
"""
_MIGRATION_V31 = """
BEGIN IMMEDIATE;
-- Automatic Memory extraction can target Space and User scopes. A shared
-- scope is fed by multiple independent Project History streams, so its
-- extraction cursor must be keyed by the source Project rather than stored as
-- one ambiguous scope-wide watermark.
CREATE TABLE IF NOT EXISTS memory_project_scope_bindings(
project_id TEXT PRIMARY KEY,
space_id TEXT NOT NULL,
user_id TEXT,
updated_at REAL NOT NULL
);
CREATE TABLE IF NOT EXISTS memory_extraction_watermarks(
target_scope_type TEXT NOT NULL CHECK(
target_scope_type IN ('space', 'user')
),
target_scope_id TEXT NOT NULL,
source_project_id TEXT NOT NULL,
processed_through_watermark TEXT,
watermark_kind TEXT CHECK(
watermark_kind IS NULL OR
watermark_kind IN ('journal_cursor', 'cloud_cursor')
),
extractor_version TEXT NOT NULL DEFAULT 'memory-v2',
last_error TEXT,
updated_at REAL NOT NULL,
PRIMARY KEY(target_scope_type, target_scope_id, source_project_id)
);
CREATE INDEX IF NOT EXISTS memory_extraction_watermarks_source_idx
ON memory_extraction_watermarks(source_project_id, target_scope_type,
target_scope_id);
-- Product policy: automatic extraction is available out of the box at every
-- Memory scope. Users can still disable an individual scope and can edit or
-- archive every extracted entry from Memory Center.
UPDATE memory_scope_state SET capture_enabled = 1;
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (31, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 31;
COMMIT;
"""
_MIGRATION_V32 = """
BEGIN IMMEDIATE;
-- A Project normally binds to the Space's primary checkout. Multiple Projects
-- may therefore share one checkout_id and are serialized by the writer lease
-- below. An explicit user branch/worktree receives a distinct checkout_id.
CREATE TABLE IF NOT EXISTS project_workspace_bindings(
project_id TEXT PRIMARY KEY,
repository_id TEXT NOT NULL REFERENCES git_repositories(repository_id)
ON DELETE CASCADE,
checkout_id TEXT NOT NULL,
checkout_mode TEXT NOT NULL CHECK(
checkout_mode IN ('primary_checkout', 'explicit_worktree')
),
target_ref TEXT NOT NULL,
worktree_path TEXT NOT NULL,
version INTEGER NOT NULL DEFAULT 1 CHECK(version >= 1),
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS project_workspace_bindings_checkout_idx
ON project_workspace_bindings(repository_id, checkout_id, updated_at DESC);
-- A Project execution lease prevents two Runs in one Project. This separate
-- checkout lease prevents mutating Tasks in different Projects from writing
-- the same physical checkout concurrently. Renderer/EventBus state is only a
-- projection of these canonical rows.
CREATE TABLE IF NOT EXISTS workspace_writer_requests(
request_id TEXT PRIMARY KEY,
repository_id TEXT NOT NULL REFERENCES git_repositories(repository_id)
ON DELETE CASCADE,
checkout_id TEXT NOT NULL,
task_id TEXT NOT NULL,
project_id TEXT NOT NULL,
target_ref TEXT NOT NULL,
reason TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'queued' CHECK(
status IN ('queued', 'acquired', 'released', 'interrupted')
),
created_at REAL NOT NULL,
acquired_at REAL,
finished_at REAL,
updated_at REAL NOT NULL,
UNIQUE(repository_id, checkout_id, task_id)
);
CREATE INDEX IF NOT EXISTS workspace_writer_requests_queue_idx
ON workspace_writer_requests(
repository_id, checkout_id, status, created_at, request_id
);
CREATE TABLE IF NOT EXISTS workspace_writer_leases(
repository_id TEXT NOT NULL REFERENCES git_repositories(repository_id)
ON DELETE CASCADE,
checkout_id TEXT NOT NULL,
request_id TEXT NOT NULL UNIQUE REFERENCES workspace_writer_requests(
request_id
) ON DELETE CASCADE,
task_id TEXT NOT NULL,
project_id TEXT NOT NULL,
target_ref TEXT NOT NULL,
acquired_at REAL NOT NULL,
version INTEGER NOT NULL DEFAULT 1 CHECK(version >= 1),
PRIMARY KEY(repository_id, checkout_id)
);
INSERT OR IGNORE INTO run_journal_migrations(version, applied_at)
VALUES (32, CAST(strftime('%s', 'now') AS REAL));
PRAGMA user_version = 32;
COMMIT;
"""
class RunJournalError(RuntimeError):
"""Base error for local RunJournal operations."""
class RunNotFoundError(RunJournalError):
pass
class OptimisticConcurrencyError(RunJournalError):
pass
class IdempotencyConflictError(RunJournalError):
pass
class UnsupportedSchemaVersionError(RunJournalError):
pass
class OutboxLeaseLostError(RunJournalError):
pass
class InvalidRunTransitionError(RunJournalError):
pass
class UnsafeResumeError(RunJournalError):
def __init__(self, tool_call_ids: list[str]) -> None:
self.tool_call_ids = tuple(tool_call_ids)
super().__init__(
"resume is blocked by unresolved external side effects: "
+ ", ".join(tool_call_ids)
)
class SQLiteRunJournal:
"""Short-transaction SQLite store for Desktop-owned Run facts."""
def __init__(
self,
path: Path | None = None,
*,
busy_timeout_ms: int = 5000,
) -> None:
self.path = path or default_run_journal_path()
self.path.parent.mkdir(parents=True, exist_ok=True)
self._lock = threading.RLock()
# Durable approval rows are facts, not executable authority. A sibling
# process with the same uid must not manufacture dispatch permission by
# editing SQLite directly; only this live store can attest a decision.
self._trusted_approval_decisions: set[tuple[str, int, str]] = set()
self._trusted_attempt_permission_profiles: set[
tuple[str, str | None]
] = set()
self._trusted_approval_rules: set[str] = set()
self._connection = sqlite3.connect(
str(self.path),
timeout=busy_timeout_ms / 1000,
isolation_level=None,
check_same_thread=False,
)
self._connection.row_factory = sqlite3.Row
with self._lock:
self._connection.execute("PRAGMA foreign_keys = ON")
self._connection.execute(
f"PRAGMA busy_timeout = {busy_timeout_ms}"
)
self._connection.execute("PRAGMA journal_mode = WAL")
self._connection.execute("PRAGMA synchronous = FULL")
self._migrate()
def __enter__(self) -> SQLiteRunJournal:
return self
def __exit__(self, *_args: object) -> None:
self.close()
def close(self) -> None:
with self._lock:
self._connection.close()
@property
def schema_version(self) -> int:
with self._lock:
row = self._connection.execute("PRAGMA user_version").fetchone()
return int(row[0])
@staticmethod
def _is_sha256(value: str) -> bool:
return len(value) == 64 and not (set(value) - set("0123456789abcdef"))
def database_settings(self) -> dict[str, Any]:
with self._lock:
return {
"journal_mode": self._connection.execute(
"PRAGMA journal_mode"
).fetchone()[0],
"foreign_keys": self._connection.execute(
"PRAGMA foreign_keys"
).fetchone()[0],
"busy_timeout": self._connection.execute(
"PRAGMA busy_timeout"
).fetchone()[0],
"synchronous": self._connection.execute(
"PRAGMA synchronous"
).fetchone()[0],
}
def put_workspace_config_revision(
self,
*,
revision_id: str,
bundle_id: str,
revision_number: int,
manifest: dict[str, Any],
status: str = "validated",
created_by: str,
now: float | None = None,
) -> WorkspaceConfigRevisionRecord:
"""Insert one immutable Bundle revision or return its exact replay."""
required = {
"revision_id": revision_id,
"bundle_id": bundle_id,
"created_by": created_by,
}
for field_name, value in required.items():
if not value.strip():
raise ValueError(f"{field_name} is required")
if revision_number < 1:
raise ValueError("revision_number must be positive")
if status not in {"draft", "validated", "published", "deprecated"}:
raise ValueError("invalid workspace config revision status")
timestamp = now if now is not None else time.time()
manifest_json = canonical_json(manifest)
manifest_digest = canonical_digest(manifest)
expected = (
revision_id,
bundle_id,
revision_number,
manifest_json,
manifest_digest,
)
with self._write_transaction() as connection:
row = connection.execute(
"""
SELECT * FROM workspace_config_revisions
WHERE revision_id = ?
OR (bundle_id = ? AND revision_number = ?)
""",
(revision_id, bundle_id, revision_number),
).fetchone()
if row is not None:
actual = (
row["revision_id"],
row["bundle_id"],
int(row["revision_number"]),
row["manifest_json"],
row["manifest_digest"],
)
if actual != expected:
raise IdempotencyConflictError(
f"workspace config revision {revision_id!r} conflicts "
"with an existing revision"
)
return self._workspace_config_revision_from_row(row)
connection.execute(
"""
INSERT INTO workspace_config_revisions(
revision_id, bundle_id, revision_number,
status, manifest_json, manifest_digest,
created_by, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(
revision_id,
bundle_id,
revision_number,
status,
manifest_json,
manifest_digest,
created_by,
timestamp,
),
)
row = connection.execute(
"""
SELECT * FROM workspace_config_revisions
WHERE revision_id = ?
""",
(revision_id,),
).fetchone()
assert row is not None
return self._workspace_config_revision_from_row(row)
def get_workspace_config_revision(
self, revision_id: str
) -> WorkspaceConfigRevisionRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_config_revisions
WHERE revision_id = ?
""",
(revision_id,),
).fetchone()
return (
self._workspace_config_revision_from_row(row)
if row is not None
else None
)
def put_workspace_config_materialization(
self,
*,
materialization_id: str,
space_id: str,
revision_id: str,
config_placement: str,
state: str = "materialized",
local_override_digest: str = "",
now: float | None = None,
) -> WorkspaceConfigMaterializationRecord:
"""Record one Space-specific placement of a shared Bundle revision."""
required = {
"materialization_id": materialization_id,
"space_id": space_id,
"revision_id": revision_id,
}
for field_name, value in required.items():
if not value.strip():
raise ValueError(f"{field_name} is required")
if config_placement not in {"in_repo", "sidecar"}:
raise ValueError("invalid config_placement")
if state not in {
"pending",
"materialized",
"needs_attention",
"degraded",
}:
raise ValueError("invalid workspace config materialization state")
timestamp = now if now is not None else time.time()
expected = (
materialization_id,
space_id,
revision_id,
config_placement,
state,
local_override_digest,
)
with self._write_transaction() as connection:
revision = connection.execute(
"""
SELECT revision_id FROM workspace_config_revisions
WHERE revision_id = ?
""",
(revision_id,),
).fetchone()
if revision is None:
raise RunNotFoundError(
f"workspace config revision {revision_id!r} does not exist"
)
row = connection.execute(
"""
SELECT * FROM workspace_config_materializations
WHERE materialization_id = ?
OR (
space_id = ? AND revision_id = ?
AND local_override_digest = ?
)
""",
(
materialization_id,
space_id,
revision_id,
local_override_digest,
),
).fetchone()
if row is not None:
actual = (
row["materialization_id"],
row["space_id"],
row["revision_id"],
row["config_placement"],
row["state"],
row["local_override_digest"],
)
if actual != expected:
raise IdempotencyConflictError(
f"workspace config materialization "
f"{materialization_id!r} conflicts with an existing "
"Space installation"
)
return self._workspace_config_materialization_from_row(row)
connection.execute(
"""
INSERT INTO workspace_config_materializations(
materialization_id, space_id, revision_id,
config_placement, state, local_override_digest,
materialized_at, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
*expected,
timestamp if state == "materialized" else None,
timestamp,
timestamp,
),
)
row = connection.execute(
"""
SELECT * FROM workspace_config_materializations
WHERE materialization_id = ?
""",
(materialization_id,),
).fetchone()
assert row is not None
return self._workspace_config_materialization_from_row(row)
def get_workspace_config_materialization(
self, materialization_id: str
) -> WorkspaceConfigMaterializationRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_config_materializations
WHERE materialization_id = ?
""",
(materialization_id,),
).fetchone()
return (
self._workspace_config_materialization_from_row(row)
if row is not None
else None
)
def get_latest_workspace_config_materialization(
self, space_id: str
) -> WorkspaceConfigMaterializationRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_config_materializations
WHERE space_id = ? AND state = 'materialized'
ORDER BY updated_at DESC, materialization_id DESC
LIMIT 1
""",
(space_id,),
).fetchone()
return (
self._workspace_config_materialization_from_row(row)
if row is not None
else None
)
def get_workspace_config_draft(
self, space_id: str
) -> WorkspaceConfigDraftRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_config_drafts
WHERE space_id = ?
""",
(space_id,),
).fetchone()
return (
self._workspace_config_draft_from_row(row)
if row is not None
else None
)
def put_workspace_config_draft(
self,
*,
space_id: str,
expected_version: int,
document: dict[str, Any],
updated_by: str,
base_revision_id: str | None = None,
now: float | None = None,
) -> WorkspaceConfigDraftRecord:
"""Create or CAS-update the mutable Space configuration working copy."""
if not space_id.strip():
raise ValueError("space_id is required")
if expected_version < 0:
raise ValueError("expected_version cannot be negative")
if not updated_by.strip():
raise ValueError("updated_by is required")
timestamp = now if now is not None else time.time()
encoded = canonical_json(document)
digest = canonical_digest(document)
with self._write_transaction() as connection:
row = connection.execute(
"""
SELECT * FROM workspace_config_drafts
WHERE space_id = ?
""",
(space_id,),
).fetchone()
if row is None:
if expected_version != 0:
raise OptimisticConcurrencyError(
f"workspace configuration for {space_id!r} "
"does not exist at the expected version"
)
if base_revision_id is not None:
revision = connection.execute(
"""
SELECT revision_id FROM workspace_config_revisions
WHERE revision_id = ?
""",
(base_revision_id,),
).fetchone()
if revision is None:
raise RunNotFoundError(
f"base workspace config revision "
f"{base_revision_id!r} does not exist"
)
connection.execute(
"""
INSERT INTO workspace_config_drafts(
space_id, version, base_revision_id,
document_json, document_digest, updated_by,
created_at, updated_at
) VALUES (?, 1, ?, ?, ?, ?, ?, ?)
""",
(
space_id,
base_revision_id,
encoded,
digest,
updated_by,
timestamp,
timestamp,
),
)
else:
current_version = int(row["version"])
if current_version != expected_version:
raise OptimisticConcurrencyError(
f"workspace configuration for {space_id!r} changed"
)
if row["base_revision_id"] != base_revision_id:
raise IdempotencyConflictError(
"workspace configuration base revision cannot change "
"during autosave"
)
updated = connection.execute(
"""
UPDATE workspace_config_drafts
SET version = version + 1,
document_json = ?,
document_digest = ?,
updated_by = ?,
updated_at = ?
WHERE space_id = ? AND version = ?
""",
(
encoded,
digest,
updated_by,
timestamp,
space_id,
expected_version,
),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
f"workspace configuration for {space_id!r} changed"
)
connection.execute(
"""
INSERT INTO workspace_config_draft_assets(
space_id, draft_version, document_digest, logical_path,
content_digest, media_type, size_bytes, executable,
provenance, created_at
)
SELECT space_id, ?, ?, logical_path, content_digest,
media_type, size_bytes, executable, provenance, ?
FROM workspace_config_draft_assets
WHERE space_id = ? AND draft_version = ?
""",
(
current_version + 1,
digest,
timestamp,
space_id,
current_version,
),
)
persisted = connection.execute(
"""
SELECT * FROM workspace_config_drafts
WHERE space_id = ?
""",
(space_id,),
).fetchone()
assert persisted is not None
return self._workspace_config_draft_from_row(persisted)
def put_workspace_config_draft_from_import(
self,
*,
space_id: str,
expected_target_draft_version: int,
client_request_id: str,
document: dict[str, Any],
review_digest: str,
assets: tuple[dict[str, Any], ...],
updated_by: str,
now: float | None = None,
) -> WorkspaceConfigDraftRecord:
"""CAS an imported draft and retain its exact reviewed asset bytes.
Idempotent replay is deliberately checked before the mutable draft CAS.
A response retry therefore returns the original conversion result even
after the working draft has advanced.
"""
if not space_id.strip():
raise ValueError("space_id is required")
if expected_target_draft_version < 0:
raise ValueError(
"expected_target_draft_version cannot be negative"
)
if not client_request_id.strip():
raise ValueError("client_request_id is required")
if not updated_by.strip():
raise ValueError("updated_by is required")
if len(review_digest) != 64:
raise ValueError("review_digest must be a SHA-256 digest")
encoded = canonical_json(document)
document_digest = canonical_digest(document)
normalized_assets: list[dict[str, Any]] = []
seen_paths: set[str] = set()
for asset in assets:
logical_path = asset.get("logical_path")
content = asset.get("content")
content_digest = asset.get("content_digest")
size_bytes = asset.get("size_bytes")
if (
not isinstance(logical_path, str)
or not logical_path.startswith("bundle://")
or logical_path in seen_paths
or not isinstance(content, bytes)
or not isinstance(content_digest, str)
or len(content_digest) != 64
or hashlib.sha256(content).hexdigest() != content_digest
or size_bytes != len(content)
or not isinstance(asset.get("media_type"), str)
or not isinstance(asset.get("provenance"), str)
or not isinstance(asset.get("executable"), bool)
):
raise ValueError("imported draft asset is invalid")
seen_paths.add(logical_path)
normalized_assets.append(
{
"logical_path": logical_path,
"content_digest": content_digest,
"media_type": asset["media_type"],
"size_bytes": size_bytes,
"executable": asset["executable"],
"provenance": asset["provenance"],
"content": content,
}
)
asset_fingerprint = [
{key: item[key] for key in item if key != "content"}
for item in normalized_assets
]
request_digest = canonical_digest(
{
"space_id": space_id,
"expected_target_draft_version": expected_target_draft_version,
"document_digest": document_digest,
"review_digest": review_digest,
"assets": asset_fingerprint,
"updated_by": updated_by,
}
)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
replay = connection.execute(
"""
SELECT * FROM workspace_agent_plugin_import_requests
WHERE client_request_id = ?
""",
(client_request_id,),
).fetchone()
if replay is not None:
if replay["request_digest"] != request_digest:
raise IdempotencyConflictError(
"Agent Plugin import request id was reused"
)
return self._workspace_config_draft_from_import_request(replay)
current = connection.execute(
"SELECT * FROM workspace_config_drafts WHERE space_id = ?",
(space_id,),
).fetchone()
if current is None:
if expected_target_draft_version != 0:
raise OptimisticConcurrencyError(
f"workspace configuration for {space_id!r} changed"
)
result_version = 1
base_revision_id = None
created_at = timestamp
connection.execute(
"""
INSERT INTO workspace_config_drafts(
space_id, version, base_revision_id,
document_json, document_digest, updated_by,
created_at, updated_at
) VALUES (?, ?, NULL, ?, ?, ?, ?, ?)
""",
(
space_id,
result_version,
encoded,
document_digest,
updated_by,
timestamp,
timestamp,
),
)
else:
if int(current["version"]) != expected_target_draft_version:
raise OptimisticConcurrencyError(
f"workspace configuration for {space_id!r} changed"
)
result_version = expected_target_draft_version + 1
base_revision_id = current["base_revision_id"]
created_at = float(current["created_at"])
updated = connection.execute(
"""
UPDATE workspace_config_drafts
SET version = ?, document_json = ?, document_digest = ?,
updated_by = ?, updated_at = ?
WHERE space_id = ? AND version = ?
""",
(
result_version,
encoded,
document_digest,
updated_by,
timestamp,
space_id,
expected_target_draft_version,
),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
f"workspace configuration for {space_id!r} changed"
)
for asset in normalized_assets:
existing_blob = connection.execute(
"""
SELECT content, size_bytes
FROM workspace_config_draft_asset_blobs
WHERE content_digest = ?
""",
(asset["content_digest"],),
).fetchone()
if existing_blob is None:
connection.execute(
"""
INSERT INTO workspace_config_draft_asset_blobs(
content_digest, content, size_bytes, created_at
) VALUES (?, ?, ?, ?)
""",
(
asset["content_digest"],
asset["content"],
asset["size_bytes"],
timestamp,
),
)
elif (
bytes(existing_blob["content"]) != asset["content"]
or int(existing_blob["size_bytes"]) != asset["size_bytes"]
):
raise IdempotencyConflictError(
"content-addressed draft asset conflicts with stored bytes"
)
connection.execute(
"""
INSERT INTO workspace_config_draft_assets(
space_id, draft_version, document_digest, logical_path,
content_digest, media_type, size_bytes, executable,
provenance, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
space_id,
result_version,
document_digest,
asset["logical_path"],
asset["content_digest"],
asset["media_type"],
asset["size_bytes"],
int(asset["executable"]),
asset["provenance"],
timestamp,
),
)
connection.execute(
"""
INSERT INTO workspace_agent_plugin_import_requests(
client_request_id, space_id,
expected_target_draft_version,
expected_review_digest, requested_by, request_digest,
result_version, result_base_revision_id,
result_document_json, result_document_digest,
result_updated_by, result_created_at, result_updated_at,
created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
client_request_id,
space_id,
expected_target_draft_version,
review_digest,
updated_by,
request_digest,
result_version,
base_revision_id,
encoded,
document_digest,
updated_by,
created_at,
timestamp,
timestamp,
),
)
persisted = connection.execute(
"SELECT * FROM workspace_config_drafts WHERE space_id = ?",
(space_id,),
).fetchone()
assert persisted is not None
return self._workspace_config_draft_from_row(persisted)
def replay_workspace_config_draft_import(
self,
*,
client_request_id: str,
space_id: str,
expected_target_draft_version: int,
expected_review_digest: str,
updated_by: str,
) -> WorkspaceConfigDraftRecord | None:
"""Return a durable conversion response without rereading its source."""
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_agent_plugin_import_requests
WHERE client_request_id = ?
""",
(client_request_id,),
).fetchone()
if row is None:
return None
if (
row["space_id"] != space_id
or int(row["expected_target_draft_version"])
!= expected_target_draft_version
or row["expected_review_digest"] != expected_review_digest
or row["requested_by"] != updated_by
):
raise IdempotencyConflictError(
"Agent Plugin import request id was reused"
)
return self._workspace_config_draft_from_import_request(row)
def list_workspace_config_draft_assets(
self,
*,
space_id: str,
draft_version: int,
document_digest: str | None = None,
) -> tuple[WorkspaceConfigDraftAssetRecord, ...]:
"""Read the exact imported bytes without depending on the source path."""
query = """
SELECT a.*, b.content
FROM workspace_config_draft_assets AS a
JOIN workspace_config_draft_asset_blobs AS b
ON b.content_digest = a.content_digest
WHERE a.space_id = ? AND a.draft_version = ?
"""
parameters: list[Any] = [space_id, draft_version]
if document_digest is not None:
query += " AND a.document_digest = ?"
parameters.append(document_digest)
query += " ORDER BY a.logical_path"
with self._lock:
rows = self._connection.execute(query, parameters).fetchall()
return tuple(
WorkspaceConfigDraftAssetRecord(
space_id=row["space_id"],
draft_version=int(row["draft_version"]),
document_digest=row["document_digest"],
logical_path=row["logical_path"],
content_digest=row["content_digest"],
media_type=row["media_type"],
size_bytes=int(row["size_bytes"]),
executable=bool(row["executable"]),
provenance=row["provenance"],
content=bytes(row["content"]),
created_at=float(row["created_at"]),
)
for row in rows
)
def list_workspace_config_draft_asset_descriptors(
self,
*,
space_id: str,
draft_version: int,
document_digest: str | None = None,
) -> tuple[WorkspaceConfigDraftAssetDescriptorRecord, ...]:
"""List prepared assets without loading their BLOB content."""
query = """
SELECT * FROM workspace_config_draft_assets
WHERE space_id = ? AND draft_version = ?
"""
parameters: list[Any] = [space_id, draft_version]
if document_digest is not None:
query += " AND document_digest = ?"
parameters.append(document_digest)
query += " ORDER BY logical_path"
with self._lock:
rows = self._connection.execute(query, parameters).fetchall()
return tuple(
self._workspace_config_draft_asset_descriptor_from_row(row)
for row in rows
)
def list_workspace_config_draft_asset_descriptor_snapshots(
self,
*,
space_id: str,
document_digest: str,
) -> tuple[tuple[WorkspaceConfigDraftAssetDescriptorRecord, ...], ...]:
"""List every persisted asset snapshot for one immutable document.
A Cloud publish receipt may arrive after the mutable draft has advanced.
Recovery must therefore compare the Cloud asset set with a historical
``(draft_version, document_digest)`` snapshot, never with the current
draft version paired with the old digest.
"""
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM workspace_config_draft_assets
WHERE space_id = ? AND document_digest = ?
ORDER BY draft_version, logical_path
""",
(space_id, document_digest),
).fetchall()
snapshots: dict[
int, list[WorkspaceConfigDraftAssetDescriptorRecord]
] = {}
for row in rows:
descriptor = (
self._workspace_config_draft_asset_descriptor_from_row(row)
)
snapshots.setdefault(descriptor.draft_version, []).append(
descriptor
)
return tuple(
tuple(snapshots[version]) for version in sorted(snapshots)
)
def get_workspace_config_draft_asset(
self,
*,
space_id: str,
draft_version: int,
document_digest: str,
logical_path: str,
content_digest: str,
) -> WorkspaceConfigDraftAssetRecord | None:
"""Load one exact prepared asset for bounded Brain-to-Cloud upload."""
with self._lock:
row = self._connection.execute(
"""
SELECT a.*, b.content
FROM workspace_config_draft_assets AS a
JOIN workspace_config_draft_asset_blobs AS b
ON b.content_digest = a.content_digest
WHERE a.space_id = ?
AND a.draft_version = ?
AND a.document_digest = ?
AND a.logical_path = ?
AND a.content_digest = ?
""",
(
space_id,
draft_version,
document_digest,
logical_path,
content_digest,
),
).fetchone()
if row is None:
return None
return WorkspaceConfigDraftAssetRecord(
space_id=row["space_id"],
draft_version=int(row["draft_version"]),
document_digest=row["document_digest"],
logical_path=row["logical_path"],
content_digest=row["content_digest"],
media_type=row["media_type"],
size_bytes=int(row["size_bytes"]),
executable=bool(row["executable"]),
provenance=row["provenance"],
content=bytes(row["content"]),
created_at=float(row["created_at"]),
)
def finalize_workspace_config_publish(
self,
*,
space_id: str,
expected_draft_version: int,
revision_id: str,
manifest_digest: str,
published_manifest: dict[str, Any],
actor_id: str,
now: float | None = None,
) -> tuple[
WorkspaceConfigRevisionRecord,
WorkspaceConfigDraftRecord,
]:
"""Record a Cloud-published revision without activating it locally."""
if len(manifest_digest) != 64:
raise ValueError("manifest_digest must be a SHA-256 digest")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
draft = connection.execute(
"SELECT * FROM workspace_config_drafts WHERE space_id = ?",
(space_id,),
).fetchone()
if draft is None:
raise RunNotFoundError(
f"workspace configuration for {space_id!r} does not exist"
)
if int(draft["version"]) < expected_draft_version:
raise OptimisticConcurrencyError(
f"workspace configuration for {space_id!r} changed"
)
if (
not revision_id.startswith("wbr_")
or len(revision_id) != 36
or set(revision_id[4:]) - set("0123456789abcdef")
):
raise ValueError("invalid revision_id")
published_json = canonical_json(published_manifest)
if canonical_digest(published_manifest) != manifest_digest:
raise IdempotencyConflictError(
"published manifest digest does not match its content"
)
published_metadata = published_manifest.get("metadata", {})
if (
not isinstance(published_metadata.get("id"), str)
or not isinstance(published_metadata.get("revision"), int)
or isinstance(published_metadata.get("revision"), bool)
or published_metadata.get("revision", 0) < 1
):
raise IdempotencyConflictError(
"published manifest identity is invalid"
)
receipt_bundle_id = str(published_metadata["id"])
receipt_revision_number = int(published_metadata["revision"])
revision = connection.execute(
"""
SELECT * FROM workspace_config_revisions
WHERE revision_id = ?
OR (bundle_id = ? AND revision_number = ?)
""",
(revision_id, receipt_bundle_id, receipt_revision_number),
).fetchone()
expected_revision = (
revision_id,
receipt_bundle_id,
receipt_revision_number,
published_json,
manifest_digest,
)
actual_revision = (
(
revision["revision_id"],
revision["bundle_id"],
int(revision["revision_number"]),
revision["manifest_json"],
revision["manifest_digest"],
)
if revision is not None
else None
)
if revision is None:
connection.execute(
"""
INSERT INTO workspace_config_revisions(
revision_id, bundle_id, revision_number,
status, version, manifest_json, manifest_digest,
created_by, created_at
) VALUES (?, ?, ?, 'published', 0, ?, ?, ?, ?)
""",
(
revision_id,
receipt_bundle_id,
receipt_revision_number,
published_json,
manifest_digest,
actor_id,
timestamp,
),
)
elif actual_revision != expected_revision:
raise IdempotencyConflictError(
"local revision conflicts with the Cloud publish"
)
elif revision["status"] in {"draft", "validated"}:
connection.execute(
"""
UPDATE workspace_config_revisions
SET status = 'published', version = version + 1
WHERE revision_id = ?
AND status IN ('draft', 'validated')
""",
(revision_id,),
)
elif revision["status"] != "published":
raise InvalidRunTransitionError(
"local revision cannot become published"
)
if draft["base_revision_id"] != revision_id:
document = json.loads(draft["document_json"])
metadata = document.get("metadata", {})
if (
metadata.get("id") != receipt_bundle_id
or metadata.get("revision") != receipt_revision_number
):
raise IdempotencyConflictError(
"published revision cannot rebase this working copy"
)
# Whether unchanged or edited concurrently, keep the current
# working content and move it to the next revision. The Cloud
# fact remains immutable at N while local edits continue at
# N+1 instead of being stranded behind a permanent conflict.
document["metadata"]["revision"] = receipt_revision_number + 1
next_json = canonical_json(document)
next_digest = canonical_digest(document)
updated = connection.execute(
"""
UPDATE workspace_config_drafts
SET version = version + 1,
base_revision_id = ?,
document_json = ?,
document_digest = ?,
updated_by = ?,
updated_at = ?
WHERE space_id = ? AND version = ?
""",
(
revision_id,
next_json,
next_digest,
actor_id,
timestamp,
space_id,
int(draft["version"]),
),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
f"workspace configuration for {space_id!r} changed"
)
connection.execute(
"""
INSERT INTO workspace_config_draft_assets(
space_id, draft_version, document_digest, logical_path,
content_digest, media_type, size_bytes, executable,
provenance, created_at
)
SELECT space_id, ?, ?, logical_path, content_digest,
media_type, size_bytes, executable, provenance, ?
FROM workspace_config_draft_assets
WHERE space_id = ? AND draft_version = ?
""",
(
int(draft["version"]) + 1,
next_digest,
timestamp,
space_id,
int(draft["version"]),
),
)
revision = connection.execute(
"SELECT * FROM workspace_config_revisions WHERE revision_id = ?",
(revision_id,),
).fetchone()
draft = connection.execute(
"SELECT * FROM workspace_config_drafts WHERE space_id = ?",
(space_id,),
).fetchone()
assert revision is not None and draft is not None
return (
self._workspace_config_revision_from_row(revision),
self._workspace_config_draft_from_row(draft),
)
def put_workspace_bundle_install_proposal(
self,
*,
proposal_id: str,
request_id: str,
space_id: str,
bundle_id: str,
revision_id: str,
config_placement: str,
manifest: dict[str, Any],
assets: list[dict[str, Any]],
install_plan: dict[str, Any],
now: float | None = None,
) -> WorkspaceBundleInstallProposalRecord:
"""Persist a reviewable install proposal without granting anything."""
if any(
not value.strip()
for value in (
proposal_id,
request_id,
space_id,
bundle_id,
revision_id,
)
):
raise ValueError("Bundle install proposal identity is required")
if config_placement not in {"in_repo", "sidecar"}:
raise ValueError("invalid config_placement")
timestamp = now if now is not None else time.time()
manifest_json = canonical_json(manifest)
manifest_digest = canonical_digest(manifest)
assets_json = canonical_json(assets)
plan_json = canonical_json(install_plan)
expected = (
proposal_id,
request_id,
space_id,
bundle_id,
revision_id,
config_placement,
manifest_json,
manifest_digest,
assets_json,
plan_json,
)
with self._write_transaction() as connection:
row = connection.execute(
"""
SELECT * FROM workspace_bundle_install_proposals
WHERE proposal_id = ? OR request_id = ?
""",
(proposal_id, request_id),
).fetchone()
if row is not None:
actual = (
row["proposal_id"],
row["request_id"],
row["space_id"],
row["bundle_id"],
row["revision_id"],
row["config_placement"],
row["manifest_json"],
row["manifest_digest"],
row["assets_json"],
row["install_plan_json"],
)
if actual != expected:
raise IdempotencyConflictError(
"Bundle install request was reused with another payload"
)
return self._workspace_bundle_install_proposal_from_row(row)
connection.execute(
"""
INSERT INTO workspace_bundle_install_proposals(
proposal_id, request_id, space_id, bundle_id,
revision_id, config_placement, state, version,
manifest_json, manifest_digest, assets_json,
install_plan_json, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, 'proposed', 0, ?, ?, ?, ?, ?, ?)
""",
(
proposal_id,
request_id,
space_id,
bundle_id,
revision_id,
config_placement,
manifest_json,
manifest_digest,
assets_json,
plan_json,
timestamp,
timestamp,
),
)
row = connection.execute(
"""SELECT * FROM workspace_bundle_install_proposals
WHERE proposal_id = ?""",
(proposal_id,),
).fetchone()
assert row is not None
return self._workspace_bundle_install_proposal_from_row(row)
def get_workspace_bundle_install_proposal(
self, proposal_id: str
) -> WorkspaceBundleInstallProposalRecord | None:
with self._lock:
row = self._connection.execute(
"""SELECT * FROM workspace_bundle_install_proposals
WHERE proposal_id = ?""",
(proposal_id,),
).fetchone()
return (
self._workspace_bundle_install_proposal_from_row(row)
if row is not None
else None
)
def get_materialized_workspace_bundle_proposal(
self, *, space_id: str, revision_id: str
) -> WorkspaceBundleInstallProposalRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_bundle_install_proposals
WHERE space_id = ? AND revision_id = ?
AND state = 'materialized'
ORDER BY updated_at DESC, proposal_id DESC
LIMIT 1
""",
(space_id, revision_id),
).fetchone()
return (
self._workspace_bundle_install_proposal_from_row(row)
if row is not None
else None
)
def get_active_workspace_bundle_proposal(
self, *, space_id: str, revision_id: str
) -> WorkspaceBundleInstallProposalRecord | None:
"""Return the proposal that currently controls an installed revision.
Review-only proposals are deliberately excluded: creating a second
proposal must not disable a currently usable installation. Once an
approved proposal begins materialization, its state becomes an
admission gate until materialization completes.
"""
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_bundle_install_proposals
WHERE space_id = ? AND revision_id = ?
AND state IN (
'materializing', 'materialized', 'needs_attention'
)
ORDER BY updated_at DESC, proposal_id DESC
LIMIT 1
""",
(space_id, revision_id),
).fetchone()
return (
self._workspace_bundle_install_proposal_from_row(row)
if row is not None
else None
)
def get_latest_workspace_bundle_install_proposal(
self,
*,
space_id: str,
) -> WorkspaceBundleInstallProposalRecord | None:
"""Return the durable install/setup flow currently owned by a Space."""
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_bundle_install_proposals
WHERE space_id = ? AND state != 'rejected'
ORDER BY updated_at DESC, proposal_id DESC
LIMIT 1
""",
(space_id,),
).fetchone()
return (
self._workspace_bundle_install_proposal_from_row(row)
if row is not None
else None
)
def transition_workspace_bundle_install_proposal(
self,
proposal_id: str,
*,
expected_version: int,
state: str,
decided_by: str | None = None,
error_code: str | None = None,
now: float | None = None,
) -> WorkspaceBundleInstallProposalRecord:
allowed = {
"proposed": {"approved", "rejected"},
"approved": {"materializing", "rejected"},
"materializing": {"materialized", "needs_attention"},
"needs_attention": {"materializing", "rejected"},
"materialized": set(),
"rejected": set(),
}
if state not in allowed:
raise ValueError("invalid Bundle install proposal state")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"""SELECT * FROM workspace_bundle_install_proposals
WHERE proposal_id = ?""",
(proposal_id,),
).fetchone()
if row is None:
raise RunNotFoundError(
f"Bundle install proposal {proposal_id!r} does not exist"
)
if row["state"] == state:
if state in {"approved", "rejected"} and (
not decided_by or row["decided_by"] != decided_by
):
raise IdempotencyConflictError(
"Bundle install decision actor does not match replay"
)
return self._workspace_bundle_install_proposal_from_row(row)
if int(row["version"]) != expected_version:
raise OptimisticConcurrencyError(
f"Bundle install proposal {proposal_id!r} changed"
)
if state not in allowed[row["state"]]:
raise InvalidRunTransitionError(
f"Bundle install proposal cannot move from "
f"{row['state']!r} to {state!r}"
)
decision_actor = row["decided_by"]
decision_at = row["decided_at"]
if state in {"approved", "rejected"}:
if not decided_by or not decided_by.strip():
raise ValueError(
"decided_by is required for user decision"
)
decision_actor = decided_by
decision_at = timestamp
updated = connection.execute(
"""
UPDATE workspace_bundle_install_proposals
SET state = ?, version = version + 1,
decided_by = ?, decided_at = ?, error_code = ?,
updated_at = ?
WHERE proposal_id = ? AND version = ? AND state = ?
""",
(
state,
decision_actor,
decision_at,
error_code,
timestamp,
proposal_id,
expected_version,
row["state"],
),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
f"Bundle install proposal {proposal_id!r} changed"
)
row = connection.execute(
"""SELECT * FROM workspace_bundle_install_proposals
WHERE proposal_id = ?""",
(proposal_id,),
).fetchone()
assert row is not None
return self._workspace_bundle_install_proposal_from_row(row)
def put_workspace_bundle_local_binding(
self,
*,
proposal_id: str,
expected_proposal_version: int,
slot_id: str,
binding_kind: str,
connector_id: str | None,
opaque_connection_id: str | None,
local_path: str | None,
required_grants: list[str],
authorized_by: str,
now: float | None = None,
) -> tuple[
WorkspaceBundleLocalBindingRecord,
WorkspaceBundleInstallProposalRecord,
]:
if binding_kind not in {"connector", "local_path", "script_approval"}:
raise ValueError("invalid Bundle local binding kind")
if not slot_id.strip() or not authorized_by.strip():
raise ValueError("binding slot and authorizer are required")
if binding_kind == "connector" and (
not connector_id or not opaque_connection_id
):
raise ValueError(
"connector binding requires connector and connection ids"
)
if binding_kind == "local_path" and not local_path:
raise ValueError("local path binding requires a path")
if binding_kind == "script_approval" and any(
value is not None
for value in (connector_id, opaque_connection_id, local_path)
):
raise ValueError("script approval cannot carry a resource binding")
binding_id = (
"bundlebind_"
+ canonical_digest(
{"proposal_id": proposal_id, "slot_id": slot_id}
)[:32]
)
grants_json = canonical_json(sorted(set(required_grants)))
expected = (
binding_id,
proposal_id,
slot_id,
binding_kind,
connector_id,
opaque_connection_id,
local_path,
grants_json,
authorized_by,
)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
proposal = connection.execute(
"""SELECT * FROM workspace_bundle_install_proposals
WHERE proposal_id = ?""",
(proposal_id,),
).fetchone()
if proposal is None:
raise RunNotFoundError(
f"Bundle install proposal {proposal_id!r} does not exist"
)
row = connection.execute(
"""SELECT * FROM workspace_bundle_local_bindings
WHERE proposal_id = ? AND slot_id = ?""",
(proposal_id, slot_id),
).fetchone()
if row is not None:
actual_resource = (
row["binding_kind"],
row["connector_id"],
row["opaque_connection_id"],
row["local_path"],
row["required_grants_json"],
)
desired_resource = (
binding_kind,
connector_id,
opaque_connection_id,
local_path,
grants_json,
)
if actual_resource == desired_resource:
return (
self._workspace_bundle_local_binding_from_row(row),
self._workspace_bundle_install_proposal_from_row(
proposal
),
)
if int(proposal["version"]) != expected_proposal_version:
raise OptimisticConcurrencyError(
f"Bundle install proposal {proposal_id!r} changed"
)
if proposal["state"] not in {
"approved",
"needs_attention",
"materialized",
}:
raise InvalidRunTransitionError(
"Bundle resources can only be rebound after approval"
)
connection.execute(
"""UPDATE workspace_bundle_local_bindings
SET binding_kind = ?, connector_id = ?,
opaque_connection_id = ?, local_path = ?,
required_grants_json = ?, authorized_by = ?,
authorized_at = ?
WHERE binding_id = ?""",
(
binding_kind,
connector_id,
opaque_connection_id,
local_path,
grants_json,
authorized_by,
timestamp,
binding_id,
),
)
connection.execute(
"""UPDATE workspace_bundle_install_proposals
SET version = version + 1,
state = CASE WHEN state = 'materialized'
THEN 'needs_attention' ELSE state END,
error_code = CASE WHEN state = 'materialized'
THEN 'bundle_reconfiguration_pending'
ELSE error_code END,
updated_at = ?
WHERE proposal_id = ? AND version = ?""",
(timestamp, proposal_id, expected_proposal_version),
)
row = connection.execute(
"""SELECT * FROM workspace_bundle_local_bindings
WHERE binding_id = ?""",
(binding_id,),
).fetchone()
proposal = connection.execute(
"""SELECT * FROM workspace_bundle_install_proposals
WHERE proposal_id = ?""",
(proposal_id,),
).fetchone()
assert row is not None and proposal is not None
return (
self._workspace_bundle_local_binding_from_row(row),
self._workspace_bundle_install_proposal_from_row(proposal),
)
if int(proposal["version"]) != expected_proposal_version:
raise OptimisticConcurrencyError(
f"Bundle install proposal {proposal_id!r} changed"
)
if proposal["state"] not in {
"approved",
"needs_attention",
"materialized",
}:
raise InvalidRunTransitionError(
"Bundle resources can only be bound after approval"
)
connection.execute(
"""
INSERT INTO workspace_bundle_local_bindings(
binding_id, proposal_id, slot_id, binding_kind,
connector_id, opaque_connection_id, local_path,
required_grants_json, authorized_by, authorized_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(*expected, timestamp),
)
connection.execute(
"""UPDATE workspace_bundle_install_proposals
SET version = version + 1,
state = CASE WHEN state = 'materialized'
THEN 'needs_attention' ELSE state END,
error_code = CASE WHEN state = 'materialized'
THEN 'bundle_reconfiguration_pending'
ELSE error_code END,
updated_at = ?
WHERE proposal_id = ? AND version = ?""",
(timestamp, proposal_id, expected_proposal_version),
)
row = connection.execute(
"""SELECT * FROM workspace_bundle_local_bindings
WHERE binding_id = ?""",
(binding_id,),
).fetchone()
proposal = connection.execute(
"""SELECT * FROM workspace_bundle_install_proposals
WHERE proposal_id = ?""",
(proposal_id,),
).fetchone()
assert row is not None and proposal is not None
return (
self._workspace_bundle_local_binding_from_row(row),
self._workspace_bundle_install_proposal_from_row(proposal),
)
def list_workspace_bundle_local_bindings(
self, proposal_id: str
) -> tuple[WorkspaceBundleLocalBindingRecord, ...]:
with self._lock:
rows = self._connection.execute(
"""SELECT * FROM workspace_bundle_local_bindings
WHERE proposal_id = ? ORDER BY slot_id""",
(proposal_id,),
).fetchall()
return tuple(
self._workspace_bundle_local_binding_from_row(row)
for row in rows
)
def put_workspace_bundle_secret_bindings(
self,
*,
proposal_id: str,
client_request_id: str,
expected_proposal_version: int,
bindings: list[dict[str, Any]],
authorized_by: str,
now: float | None = None,
) -> tuple[
tuple[WorkspaceBundleSecretBindingRecord, ...],
WorkspaceBundleInstallProposalRecord,
]:
"""CAS opaque vault references without ever accepting secret values."""
if not client_request_id.strip() or not authorized_by.strip():
raise ValueError("binding request and authorizer are required")
if not bindings:
raise ValueError("at least one secret binding is required")
normalized: list[dict[str, Any]] = []
keys: set[str] = set()
allowed_ref_characters = (
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
)
for item in bindings:
requirement_key = str(item.get("requirement_key") or "")
requirement_kind = str(item.get("requirement_kind") or "")
secret_ref = str(item.get("secret_ref") or "")
account_scope_digest = str(item.get("account_scope_digest") or "")
expected_binding_version = item.get("expected_binding_version")
if expected_binding_version is not None:
expected_binding_version = int(expected_binding_version)
if expected_binding_version < 1:
raise ValueError("invalid Bundle secret binding version")
if requirement_kind not in {"environment", "mcp_secret"}:
raise ValueError("invalid Bundle secret binding kind")
if (
not requirement_key.strip()
or len(secret_ref) != 40
or not secret_ref.startswith("wsvault_")
or any(
character not in allowed_ref_characters
for character in secret_ref[8:]
)
or not self._is_sha256(account_scope_digest)
):
raise ValueError("invalid Bundle secret binding reference")
if requirement_key in keys:
raise ValueError("duplicate Bundle secret binding requirement")
keys.add(requirement_key)
normalized.append(
{
"requirement_key": requirement_key,
"requirement_kind": requirement_kind,
"secret_ref": secret_ref,
"account_scope_digest": account_scope_digest,
"expected_binding_version": expected_binding_version,
}
)
normalized.sort(key=lambda item: str(item["requirement_key"]))
request_digest = canonical_digest(
{
"proposal_id": proposal_id,
"bindings": normalized,
"authorized_by": authorized_by,
}
)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
receipt = connection.execute(
"""SELECT request_digest
FROM workspace_bundle_secret_binding_requests
WHERE client_request_id = ?""",
(client_request_id,),
).fetchone()
if receipt is not None:
if receipt["request_digest"] != request_digest:
raise IdempotencyConflictError(
"Bundle secret binding request id was reused"
)
rows = connection.execute(
"""SELECT * FROM workspace_bundle_secret_bindings
WHERE proposal_id = ? AND requirement_key IN (
SELECT value FROM json_each(?)
) ORDER BY requirement_key""",
(
proposal_id,
json.dumps(
[item["requirement_key"] for item in normalized],
separators=(",", ":"),
),
),
).fetchall()
proposal = connection.execute(
"""SELECT * FROM workspace_bundle_install_proposals
WHERE proposal_id = ?""",
(proposal_id,),
).fetchone()
if proposal is None:
raise RunNotFoundError(
f"Bundle install proposal {proposal_id!r} does not exist"
)
return (
tuple(
self._workspace_bundle_secret_binding_from_row(row)
for row in rows
),
self._workspace_bundle_install_proposal_from_row(proposal),
)
proposal = connection.execute(
"""SELECT * FROM workspace_bundle_install_proposals
WHERE proposal_id = ?""",
(proposal_id,),
).fetchone()
if proposal is None:
raise RunNotFoundError(
f"Bundle install proposal {proposal_id!r} does not exist"
)
if int(proposal["version"]) != expected_proposal_version:
raise OptimisticConcurrencyError(
f"Bundle install proposal {proposal_id!r} changed"
)
if proposal["state"] not in {
"approved",
"needs_attention",
"materialized",
}:
raise InvalidRunTransitionError(
"Bundle values can only be bound after approval"
)
changed = False
for item in normalized:
requirement_key = str(item["requirement_key"])
existing = connection.execute(
"""SELECT * FROM workspace_bundle_secret_bindings
WHERE proposal_id = ? AND requirement_key = ?""",
(proposal_id, requirement_key),
).fetchone()
expected_binding_version = item["expected_binding_version"]
if existing is None:
if expected_binding_version is not None:
raise OptimisticConcurrencyError(
f"Bundle value {requirement_key!r} changed"
)
binding_id = (
"bundlesecret_"
+ canonical_digest(
{
"proposal_id": proposal_id,
"requirement_key": requirement_key,
}
)[:32]
)
connection.execute(
"""INSERT INTO workspace_bundle_secret_bindings(
binding_id, proposal_id, requirement_key,
requirement_kind, binding_version, secret_ref,
account_scope_digest, authorized_by,
authorized_at, updated_at
) VALUES (?, ?, ?, ?, 1, ?, ?, ?, ?, ?)""",
(
binding_id,
proposal_id,
requirement_key,
item["requirement_kind"],
item["secret_ref"],
item["account_scope_digest"],
authorized_by,
timestamp,
timestamp,
),
)
changed = True
continue
if (
expected_binding_version is not None
and int(existing["binding_version"])
!= expected_binding_version
):
raise OptimisticConcurrencyError(
f"Bundle value {requirement_key!r} changed"
)
desired = (
item["requirement_kind"],
item["secret_ref"],
item["account_scope_digest"],
)
actual = (
existing["requirement_kind"],
existing["secret_ref"],
existing["account_scope_digest"],
)
if desired == actual:
continue
if expected_binding_version is None:
raise IdempotencyConflictError(
f"Bundle value {requirement_key!r} already exists"
)
connection.execute(
"""UPDATE workspace_bundle_secret_bindings
SET requirement_kind = ?, binding_version = binding_version + 1,
secret_ref = ?,
account_scope_digest = ?, authorized_by = ?,
authorized_at = ?, updated_at = ?
WHERE binding_id = ?""",
(
item["requirement_kind"],
item["secret_ref"],
item["account_scope_digest"],
authorized_by,
timestamp,
timestamp,
existing["binding_id"],
),
)
changed = True
connection.execute(
"""INSERT INTO workspace_bundle_secret_binding_requests(
client_request_id, proposal_id, request_digest, created_at
) VALUES (?, ?, ?, ?)""",
(
client_request_id,
proposal_id,
request_digest,
timestamp,
),
)
if changed:
connection.execute(
"""UPDATE workspace_bundle_install_proposals
SET version = version + 1, updated_at = ?
WHERE proposal_id = ? AND version = ?""",
(timestamp, proposal_id, expected_proposal_version),
)
rows = connection.execute(
"""SELECT * FROM workspace_bundle_secret_bindings
WHERE proposal_id = ? AND requirement_key IN (
SELECT value FROM json_each(?)
) ORDER BY requirement_key""",
(
proposal_id,
json.dumps(
[item["requirement_key"] for item in normalized],
separators=(",", ":"),
),
),
).fetchall()
proposal = connection.execute(
"""SELECT * FROM workspace_bundle_install_proposals
WHERE proposal_id = ?""",
(proposal_id,),
).fetchone()
assert proposal is not None
return (
tuple(
self._workspace_bundle_secret_binding_from_row(row)
for row in rows
),
self._workspace_bundle_install_proposal_from_row(proposal),
)
def list_workspace_bundle_secret_bindings(
self, proposal_id: str
) -> tuple[WorkspaceBundleSecretBindingRecord, ...]:
with self._lock:
rows = self._connection.execute(
"""SELECT * FROM workspace_bundle_secret_bindings
WHERE proposal_id = ? ORDER BY requirement_key""",
(proposal_id,),
).fetchall()
return tuple(
self._workspace_bundle_secret_binding_from_row(row)
for row in rows
)
def transition_workspace_config_revision(
self,
revision_id: str,
*,
expected_version: int,
status: str,
) -> WorkspaceConfigRevisionRecord:
"""CAS the Bundle lifecycle without making its manifest mutable."""
allowed = {
"draft": {"validated"},
"validated": {"published"},
"published": {"deprecated"},
"deprecated": set(),
}
if status not in allowed:
raise ValueError("invalid workspace config revision status")
with self._write_transaction() as connection:
row = connection.execute(
"""
SELECT * FROM workspace_config_revisions
WHERE revision_id = ?
""",
(revision_id,),
).fetchone()
if row is None:
raise RunNotFoundError(
f"workspace config revision {revision_id!r} does not exist"
)
if int(row["version"]) != expected_version:
raise OptimisticConcurrencyError(
f"workspace config revision {revision_id!r} expected "
f"version {expected_version}, found {row['version']}"
)
if row["status"] == status:
return self._workspace_config_revision_from_row(row)
if status not in allowed[row["status"]]:
raise InvalidRunTransitionError(
f"workspace config revision {revision_id!r} cannot move "
f"from {row['status']!r} to {status!r}"
)
updated = connection.execute(
"""
UPDATE workspace_config_revisions
SET status = ?, version = version + 1
WHERE revision_id = ? AND version = ? AND status = ?
""",
(
status,
revision_id,
expected_version,
row["status"],
),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
f"workspace config revision {revision_id!r} changed "
"during transition"
)
row = connection.execute(
"""
SELECT * FROM workspace_config_revisions
WHERE revision_id = ?
""",
(revision_id,),
).fetchone()
assert row is not None
return self._workspace_config_revision_from_row(row)
def put_effective_environment_spec(
self,
spec: EffectiveEnvironmentSpec,
*,
emit_run_event: bool = False,
now: float | None = None,
) -> EffectiveEnvironmentSpecRecord:
"""Persist immutable local and redacted forms in one transaction."""
timestamp = now if now is not None else time.time()
local_payload = spec.local_payload()
if canonical_digest(spec.semantic_spec) != spec.semantic_spec_digest:
raise IdempotencyConflictError(
"semantic EnvironmentSpec digest does not match its payload"
)
local_materialization_payload = spec.local_materialization.model_dump(
exclude_none=True,
mode="json",
)
if (
canonical_digest(local_materialization_payload)
!= spec.local_materialization_digest
):
raise IdempotencyConflictError(
"local materialization digest does not match its payload"
)
spec_json = canonical_json(local_payload)
environment_spec_digest = spec.digest
redacted_payload = spec.cloud_projection()
projection_digest = str(redacted_payload["projection_digest"])
projection_body = {
key: value
for key, value in redacted_payload.items()
if key != "projection_digest"
}
if canonical_digest(projection_body) != projection_digest:
raise IdempotencyConflictError(
"Cloud EnvironmentSpec projection digest is invalid"
)
redacted_spec_json = canonical_json(redacted_payload)
with self._write_transaction() as connection:
revision = connection.execute(
"""
SELECT manifest_digest FROM workspace_config_revisions
WHERE revision_id = ?
""",
(spec.bundle_revision_id,),
).fetchone()
if revision is None:
raise RunNotFoundError(
f"workspace config revision "
f"{spec.bundle_revision_id!r} does not exist"
)
if revision["manifest_digest"] != spec.manifest_digest:
raise IdempotencyConflictError(
"EnvironmentSpec manifest digest does not match its "
"workspace config revision"
)
expected = (
spec.spec_id,
spec.owner_type,
spec.owner_id,
spec.bundle_revision_id,
spec.manifest_digest,
spec_json,
environment_spec_digest,
spec.semantic_spec_digest,
spec.local_materialization_digest,
redacted_spec_json,
projection_digest,
spec.permission_profile_revision,
spec.provider_capability_revision,
)
row = connection.execute(
"""
SELECT * FROM effective_environment_specs
WHERE environment_spec_id = ?
""",
(spec.spec_id,),
).fetchone()
if row is not None:
actual = (
row["environment_spec_id"],
row["owner_type"],
row["owner_id"],
row["bundle_revision_id"],
row["manifest_digest"],
row["spec_json"],
row["environment_spec_digest"],
row["semantic_spec_digest"],
row["local_materialization_digest"],
row["redacted_spec_json"],
row["projection_digest"],
row["permission_profile_revision"],
row["provider_capability_revision"],
)
if actual != expected:
raise IdempotencyConflictError(
f"EnvironmentSpec {spec.spec_id!r} conflicts with "
"an existing immutable spec"
)
if emit_run_event:
self._append_environment_resolved_event(
connection,
spec,
redacted_payload,
timestamp,
)
return self._effective_environment_spec_from_row(row)
connection.execute(
"""
INSERT INTO effective_environment_specs(
environment_spec_id, owner_type, owner_id,
bundle_revision_id, manifest_digest, spec_json,
environment_spec_digest, semantic_spec_digest,
local_materialization_digest, redacted_spec_json,
projection_digest, permission_profile_revision,
provider_capability_revision, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(*expected, timestamp),
)
if emit_run_event:
self._append_environment_resolved_event(
connection,
spec,
redacted_payload,
timestamp,
)
row = connection.execute(
"""
SELECT * FROM effective_environment_specs
WHERE environment_spec_id = ?
""",
(spec.spec_id,),
).fetchone()
assert row is not None
return self._effective_environment_spec_from_row(row)
def _append_environment_resolved_event(
self,
connection: sqlite3.Connection,
spec: EffectiveEnvironmentSpec,
redacted_payload: dict[str, Any],
timestamp: float,
) -> None:
if spec.owner_type != "run":
raise ValueError(
"run.environment_resolved requires a Run-owned spec"
)
self._append_event_in_transaction(
connection,
spec.owner_id,
RunEventDraft(
event_id=f"environment:{spec.spec_id}:resolved",
event_type="run.environment_resolved",
payload=redacted_payload,
created_at=timestamp,
),
)
def get_effective_environment_spec(
self, environment_spec_id: str
) -> EffectiveEnvironmentSpecRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM effective_environment_specs
WHERE environment_spec_id = ?
""",
(environment_spec_id,),
).fetchone()
return (
self._effective_environment_spec_from_row(row)
if row is not None
else None
)
def put_git_repository(
self,
*,
repository_id: str,
space_id: str,
repository_role: str,
root_path: str,
root_path_digest: str,
ownership: str,
state: str,
version_coverage: str,
hooks_mode: str = "disabled",
repo_subdir: str | None = None,
now: float | None = None,
) -> GitRepositoryRecord:
timestamp = now if now is not None else time.time()
immutable_expected = (
repository_id,
space_id,
repository_role,
root_path,
root_path_digest,
ownership,
version_coverage,
hooks_mode,
repo_subdir,
)
with self._write_transaction() as connection:
by_identity = connection.execute(
"SELECT * FROM git_repositories WHERE repository_id = ?",
(repository_id,),
).fetchone()
by_role = connection.execute(
"""
SELECT * FROM git_repositories
WHERE space_id = ? AND repository_role = ?
""",
(space_id, repository_role),
).fetchone()
row = by_identity or by_role
if row is not None:
actual = (
row["repository_id"],
row["space_id"],
row["repository_role"],
row["root_path"],
row["root_path_digest"],
row["ownership"],
row["version_coverage"],
row["hooks_mode"],
row["repo_subdir"],
)
if actual != immutable_expected:
raise IdempotencyConflictError(
f"Git repository ownership for Space {space_id!r} "
"conflicts with the persisted binding"
)
if row["state"] != state:
connection.execute(
"""
UPDATE git_repositories
SET state = ?, version = version + 1, updated_at = ?
WHERE repository_id = ?
""",
(state, timestamp, row["repository_id"]),
)
row = connection.execute(
"""
SELECT * FROM git_repositories
WHERE repository_id = ?
""",
(row["repository_id"],),
).fetchone()
assert row is not None
return self._git_repository_from_row(row)
connection.execute(
"""
INSERT INTO git_repositories(
repository_id, space_id, repository_role, root_path,
root_path_digest, ownership, state, version_coverage,
hooks_mode, repo_subdir, version, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)
""",
(
*immutable_expected[:6],
state,
*immutable_expected[6:],
timestamp,
timestamp,
),
)
row = connection.execute(
"SELECT * FROM git_repositories WHERE repository_id = ?",
(repository_id,),
).fetchone()
assert row is not None
return self._git_repository_from_row(row)
def update_git_repository_state(
self,
repository_id: str,
*,
state: str,
expected_version: int,
now: float | None = None,
) -> GitRepositoryRecord:
if state not in {
"ready",
"not_enabled",
"needs_attention",
"degraded",
}:
raise ValueError(f"unsupported Git repository state {state!r}")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM git_repositories WHERE repository_id = ?",
(repository_id,),
).fetchone()
if row is None:
raise ValueError(f"unknown Git repository {repository_id!r}")
if row["state"] == state:
return self._git_repository_from_row(row)
if int(row["version"]) != expected_version:
raise IdempotencyConflictError(
f"Git repository {repository_id!r} changed concurrently"
)
connection.execute(
"""
UPDATE git_repositories
SET state = ?, version = version + 1, updated_at = ?
WHERE repository_id = ? AND version = ?
""",
(state, timestamp, repository_id, expected_version),
)
row = connection.execute(
"SELECT * FROM git_repositories WHERE repository_id = ?",
(repository_id,),
).fetchone()
assert row is not None
return self._git_repository_from_row(row)
def get_git_repository(
self, repository_id: str
) -> GitRepositoryRecord | None:
with self._lock:
row = self._connection.execute(
"SELECT * FROM git_repositories WHERE repository_id = ?",
(repository_id,),
).fetchone()
return (
self._git_repository_from_row(row) if row is not None else None
)
def get_space_git_repository(
self,
*,
space_id: str,
repository_role: str = "content",
) -> GitRepositoryRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM git_repositories
WHERE space_id = ? AND repository_role = ?
""",
(space_id, repository_role),
).fetchone()
return (
self._git_repository_from_row(row) if row is not None else None
)
def list_git_repositories(self) -> list[GitRepositoryRecord]:
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM git_repositories
ORDER BY created_at, repository_id
"""
).fetchall()
return [self._git_repository_from_row(row) for row in rows]
def begin_git_operation(
self,
*,
operation_id: str,
repository_id: str,
request_id: str,
operation_type: str,
payload_digest: str,
expected_repo_state_digest: str | None,
now: float | None = None,
) -> GitOperationRecord:
if len(payload_digest) != 64:
raise ValueError("Git operation payload digest must be SHA-256")
timestamp = now if now is not None else time.time()
expected = (
operation_id,
repository_id,
request_id,
operation_type,
payload_digest,
expected_repo_state_digest,
)
with self._write_transaction() as connection:
if (
connection.execute(
"SELECT 1 FROM git_repositories WHERE repository_id = ?",
(repository_id,),
).fetchone()
is None
):
raise ValueError(f"unknown Git repository {repository_id!r}")
row = connection.execute(
"""
SELECT * FROM git_operations
WHERE operation_id = ? OR (
repository_id = ? AND request_id = ?
)
""",
(operation_id, repository_id, request_id),
).fetchone()
if row is not None:
actual = (
row["operation_id"],
row["repository_id"],
row["request_id"],
row["operation_type"],
row["payload_digest"],
row["expected_repo_state_digest"],
)
if actual != expected:
raise IdempotencyConflictError(
f"Git operation request {request_id!r} was reused "
"with a different action"
)
return self._git_operation_from_row(row)
connection.execute(
"""
INSERT INTO git_operations(
operation_id, repository_id, request_id,
operation_type, payload_digest, status,
expected_repo_state_digest, observed_repo_state_digest,
result_json, error_code, error_message,
created_at, updated_at
) VALUES (?, ?, ?, ?, ?, 'prepared', ?, NULL, NULL, NULL,
NULL, ?, ?)
""",
(*expected, timestamp, timestamp),
)
row = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
assert row is not None
return self._git_operation_from_row(row)
def mark_git_operation_dispatched(
self,
operation_id: str,
*,
observed_repo_state_digest: str,
now: float | None = None,
) -> GitOperationRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
if row is None:
raise ValueError(f"unknown Git operation {operation_id!r}")
if row["status"] in {"dispatched", "completed"}:
return self._git_operation_from_row(row)
if row["status"] != "prepared":
raise InvalidRunTransitionError(
f"Git operation {operation_id!r} cannot dispatch from "
f"{row['status']!r}"
)
connection.execute(
"""
UPDATE git_operations
SET status = 'dispatched', observed_repo_state_digest = ?,
updated_at = ?
WHERE operation_id = ? AND status = 'prepared'
""",
(observed_repo_state_digest, timestamp, operation_id),
)
row = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
assert row is not None
return self._git_operation_from_row(row)
def complete_git_operation(
self,
operation_id: str,
*,
result: dict[str, Any],
observed_repo_state_digest: str,
now: float | None = None,
) -> GitOperationRecord:
timestamp = now if now is not None else time.time()
result_json = canonical_json(result)
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
if row is None:
raise ValueError(f"unknown Git operation {operation_id!r}")
if row["status"] == "completed":
if row["result_json"] != result_json:
raise IdempotencyConflictError(
f"Git operation {operation_id!r} completed with a "
"different result"
)
return self._git_operation_from_row(row)
if row["status"] != "dispatched":
raise InvalidRunTransitionError(
f"Git operation {operation_id!r} cannot complete from "
f"{row['status']!r}"
)
connection.execute(
"""
UPDATE git_operations
SET status = 'completed', result_json = ?,
observed_repo_state_digest = ?, error_code = NULL,
error_message = NULL, updated_at = ?
WHERE operation_id = ?
""",
(
result_json,
observed_repo_state_digest,
timestamp,
operation_id,
),
)
row = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
assert row is not None
return self._git_operation_from_row(row)
def fail_git_operation(
self,
operation_id: str,
*,
error_code: str,
error_message: str,
outcome_unknown: bool = False,
now: float | None = None,
) -> GitOperationRecord:
timestamp = now if now is not None else time.time()
target = "outcome_unknown" if outcome_unknown else "failed"
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
if row is None:
raise ValueError(f"unknown Git operation {operation_id!r}")
if row["status"] == "completed":
return self._git_operation_from_row(row)
if row["status"] in {"failed", "outcome_unknown"}:
if row["status"] == target:
return self._git_operation_from_row(row)
raise InvalidRunTransitionError(
f"Git operation {operation_id!r} cannot transition from "
f"{row['status']!r} to {target!r}"
)
if row["status"] == "prepared" and outcome_unknown:
raise InvalidRunTransitionError(
"a Git operation cannot become outcome_unknown before "
"dispatch"
)
connection.execute(
"""
UPDATE git_operations
SET status = ?, error_code = ?, error_message = ?,
updated_at = ?
WHERE operation_id = ?
""",
(
target,
error_code,
error_message,
timestamp,
operation_id,
),
)
row = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
assert row is not None
return self._git_operation_from_row(row)
def get_git_operation(
self, operation_id: str
) -> GitOperationRecord | None:
with self._lock:
row = self._connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
return (
self._git_operation_from_row(row) if row is not None else None
)
def list_git_operations(
self,
*,
statuses: tuple[str, ...] | None = None,
) -> list[GitOperationRecord]:
with self._lock:
query = "SELECT * FROM git_operations"
parameters: list[Any] = []
if statuses:
placeholders = ", ".join("?" for _ in statuses)
query += f" WHERE status IN ({placeholders})"
parameters.extend(statuses)
query += " ORDER BY created_at, operation_id"
rows = self._connection.execute(query, parameters).fetchall()
return [self._git_operation_from_row(row) for row in rows]
def complete_git_checkpoint(
self,
*,
checkpoint_id: str,
operation_id: str,
repository_id: str,
target_role: str,
target_id: str,
commit_oid: str,
parent_oid: str | None,
paths: tuple[str, ...],
managed_path_sources: dict[str, str],
actor_id: str,
trigger: str,
message: str,
observed_repo_state_digest: str,
now: float | None = None,
) -> GitCheckpointRecord:
if not paths or tuple(sorted(set(paths))) != paths:
raise ValueError(
"checkpoint paths must be non-empty, unique, and sorted"
)
if set(managed_path_sources) != set(paths):
raise ValueError(
"managed path sources must match checkpoint paths"
)
timestamp = now if now is not None else time.time()
paths_json = canonical_json(list(paths))
result = {
"checkpoint_id": checkpoint_id,
"commit_oid": commit_oid,
"parent_oid": parent_oid,
"paths": list(paths),
}
result_json = canonical_json(result)
with self._write_transaction() as connection:
operation = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
if (
operation is None
or operation["repository_id"] != repository_id
):
raise ValueError("checkpoint operation/repository mismatch")
existing = connection.execute(
"""
SELECT * FROM git_checkpoints
WHERE checkpoint_id = ? OR operation_id = ?
""",
(checkpoint_id, operation_id),
).fetchone()
if existing is not None:
expected = (
checkpoint_id,
repository_id,
operation_id,
target_role,
target_id,
commit_oid,
parent_oid,
paths_json,
actor_id,
trigger,
message,
)
actual = tuple(
existing[column]
for column in (
"checkpoint_id",
"repository_id",
"operation_id",
"target_role",
"target_id",
"commit_oid",
"parent_oid",
"paths_json",
"actor_id",
"trigger",
"message",
)
)
if actual != expected:
raise IdempotencyConflictError(
f"checkpoint {checkpoint_id!r} conflicts with its "
"persisted result"
)
return self._git_checkpoint_from_row(existing)
if operation["status"] != "dispatched":
raise InvalidRunTransitionError(
f"Git operation {operation_id!r} cannot create a "
f"checkpoint from {operation['status']!r}"
)
connection.execute(
"""
INSERT INTO git_checkpoints(
checkpoint_id, repository_id, operation_id, target_role,
target_id, commit_oid, parent_oid, paths_json, actor_id,
trigger, message, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
checkpoint_id,
repository_id,
operation_id,
target_role,
target_id,
commit_oid,
parent_oid,
paths_json,
actor_id,
trigger,
message,
timestamp,
),
)
for relative_path, source in managed_path_sources.items():
connection.execute(
"""
INSERT INTO git_managed_paths(
repository_id, relative_path, source,
first_checkpoint_id, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(repository_id, relative_path) DO UPDATE SET
updated_at = excluded.updated_at
""",
(
repository_id,
relative_path,
source,
checkpoint_id,
timestamp,
timestamp,
),
)
connection.execute(
"""
UPDATE git_operations
SET status = 'completed', result_json = ?,
observed_repo_state_digest = ?, error_code = NULL,
error_message = NULL, updated_at = ?
WHERE operation_id = ?
""",
(
result_json,
observed_repo_state_digest,
timestamp,
operation_id,
),
)
row = connection.execute(
"SELECT * FROM git_checkpoints WHERE checkpoint_id = ?",
(checkpoint_id,),
).fetchone()
assert row is not None
return self._git_checkpoint_from_row(row)
def get_git_checkpoint(
self, checkpoint_id: str
) -> GitCheckpointRecord | None:
with self._lock:
row = self._connection.execute(
"SELECT * FROM git_checkpoints WHERE checkpoint_id = ?",
(checkpoint_id,),
).fetchone()
return (
self._git_checkpoint_from_row(row) if row is not None else None
)
def list_git_checkpoints(
self,
repository_id: str,
*,
limit: int = 100,
) -> list[GitCheckpointRecord]:
if limit < 1:
raise ValueError("checkpoint query limit must be positive")
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM git_checkpoints
WHERE repository_id = ?
ORDER BY created_at DESC, checkpoint_id DESC
LIMIT ?
""",
(repository_id, limit),
).fetchall()
return [self._git_checkpoint_from_row(row) for row in rows]
def get_latest_git_checkpoint_for_target(
self,
*,
repository_id: str,
target_role: str,
target_id: str,
) -> GitCheckpointRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM git_checkpoints
WHERE repository_id = ? AND target_role = ? AND target_id = ?
ORDER BY created_at DESC, checkpoint_id DESC
LIMIT 1
""",
(repository_id, target_role, target_id),
).fetchone()
return self._git_checkpoint_from_row(row) if row else None
def list_git_managed_paths(self, repository_id: str) -> tuple[str, ...]:
with self._lock:
rows = self._connection.execute(
"""
SELECT relative_path FROM git_managed_paths
WHERE repository_id = ?
ORDER BY relative_path
""",
(repository_id,),
).fetchall()
return tuple(row["relative_path"] for row in rows)
def admit_git_run_workspace(
self,
*,
run_id: str,
project_id: str,
repository_id: str,
user_head: str | None,
user_ref: str | None,
now: float | None = None,
) -> tuple[ProjectGitStateRecord, RunGitMaterializationRecord]:
"""Pin a Run base without creating refs, branches, or worktrees."""
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
run = connection.execute(
"SELECT project_id FROM runs WHERE run_id = ?",
(run_id,),
).fetchone()
if run is None:
raise ValueError(f"unknown Run {run_id!r}")
if run["project_id"] != project_id:
raise IdempotencyConflictError(
f"Run {run_id!r} belongs to another Project"
)
repository = connection.execute(
"""
SELECT repository_role FROM git_repositories
WHERE repository_id = ?
""",
(repository_id,),
).fetchone()
if (
repository is None
or repository["repository_role"] != "content"
):
raise ValueError(
f"unknown Content Repository {repository_id!r}"
)
existing_run = connection.execute(
"""
SELECT * FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
if existing_run is not None:
if (
existing_run["project_id"] != project_id
or existing_run["repository_id"] != repository_id
):
raise IdempotencyConflictError(
f"Run {run_id!r} has another Git workspace owner"
)
project = connection.execute(
"""
SELECT * FROM git_project_integrations
WHERE project_id = ?
""",
(project_id,),
).fetchone()
if project is None:
raise RunJournalError(
"Run Git admission has no Project Git state"
)
return (
self._project_git_state_from_row(project),
self._run_git_materialization_from_row(existing_run),
)
project = connection.execute(
"""
SELECT * FROM git_project_integrations WHERE project_id = ?
""",
(project_id,),
).fetchone()
if project is None:
connection.execute(
"""
INSERT INTO git_project_integrations(
project_id, repository_id, integration_ref,
integration_head, last_synced_user_head,
pending_apply, worktree_path, projected_head,
state, version, created_at, updated_at
) VALUES (?, ?, NULL, NULL, ?, 0, NULL, NULL,
'unmaterialized', 0, ?, ?)
""",
(
project_id,
repository_id,
user_head,
timestamp,
timestamp,
),
)
project = connection.execute(
"""
SELECT * FROM git_project_integrations
WHERE project_id = ?
""",
(project_id,),
).fetchone()
else:
if project["repository_id"] != repository_id:
raise IdempotencyConflictError(
f"Project {project_id!r} belongs to another repository"
)
if (
project["integration_head"] is None
and project["last_synced_user_head"] != user_head
):
connection.execute(
"""
UPDATE git_project_integrations
SET last_synced_user_head = ?, version = version + 1,
updated_at = ?
WHERE project_id = ? AND version = ?
""",
(
user_head,
timestamp,
project_id,
int(project["version"]),
),
)
project = connection.execute(
"""
SELECT * FROM git_project_integrations
WHERE project_id = ?
""",
(project_id,),
).fetchone()
assert project is not None
base_commit = project["integration_head"] or user_head
base_ref = project["integration_ref"] or user_ref
connection.execute(
"""
INSERT INTO git_run_materializations(
run_id, project_id, repository_id, workspace_base_ref,
workspace_base_commit, project_state_version,
materialization_state, run_ref, worktree_path,
promoted_commit, version, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, 'unmaterialized', NULL, NULL,
NULL, 0, ?, ?)
""",
(
run_id,
project_id,
repository_id,
base_ref,
base_commit,
int(project["version"]),
timestamp,
timestamp,
),
)
admitted = connection.execute(
"""
SELECT * FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
assert admitted is not None
return (
self._project_git_state_from_row(project),
self._run_git_materialization_from_row(admitted),
)
def get_project_git_state(
self,
project_id: str,
) -> ProjectGitStateRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM git_project_integrations WHERE project_id = ?
""",
(project_id,),
).fetchone()
return (
self._project_git_state_from_row(row)
if row is not None
else None
)
def rebase_unmaterialized_git_run(
self,
*,
run_id: str,
expected_base_commit: str | None,
base_commit: str,
base_ref: str,
now: float | None = None,
) -> RunGitMaterializationRecord:
"""Move a direct Run boundary after preserving pre-existing dirt.
This is legal only before the Run has produced a managed ChangeSet.
It lets direct-checkout execution commit the user's visible preimage
without incorrectly attributing that preimage to the Agent diff.
"""
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
return self._rebase_unmaterialized_git_run_in_transaction(
connection,
run_id=run_id,
expected_base_commit=expected_base_commit,
base_commit=base_commit,
base_ref=base_ref,
timestamp=timestamp,
)
def refresh_git_run_base_after_writer_acquired(
self,
*,
run_id: str,
attempt_id: str,
writer_request_id: str,
expected_task_id: str,
expected_checkout_id: str,
expected_base_commit: str | None,
base_commit: str,
base_ref: str,
now: float | None = None,
) -> RunGitMaterializationRecord:
"""Atomically refresh a queued direct Run before execution starts.
Admission may happen while another Project owns the same physical
checkout. The Run can therefore become stale while it waits. Only an
acquired writer whose Attempt is still pending may move the boundary;
the audit event is committed in the same transaction as that move.
"""
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
admitted_run = connection.execute(
"""
SELECT project_id, repository_id
FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
if admitted_run is None:
raise RunNotFoundError(f"Run {run_id!r} has no Git admission")
attempt = connection.execute(
"SELECT run_id, status FROM run_attempts WHERE attempt_id = ?",
(attempt_id,),
).fetchone()
if attempt is None or attempt["run_id"] != run_id:
raise InvalidRunTransitionError(
"Run boundary refresh requires its pending Attempt"
)
if attempt["status"] != "pending":
raise InvalidRunTransitionError(
"Run boundary cannot move after Attempt activation"
)
request = connection.execute(
"""
SELECT * FROM workspace_writer_requests
WHERE request_id = ?
""",
(writer_request_id,),
).fetchone()
if (
request is None
or request["status"] != "acquired"
or request["task_id"] != expected_task_id
or request["checkout_id"] != expected_checkout_id
):
raise InvalidRunTransitionError(
"Run boundary refresh requires its acquired writer lease"
)
if (
request["project_id"] != admitted_run["project_id"]
or request["repository_id"] != admitted_run["repository_id"]
):
raise InvalidRunTransitionError(
"Run boundary writer does not own its Git admission"
)
lease = connection.execute(
"""
SELECT request_id FROM workspace_writer_leases
WHERE repository_id = ? AND checkout_id = ?
""",
(request["repository_id"], request["checkout_id"]),
).fetchone()
if lease is None or lease["request_id"] != writer_request_id:
raise InvalidRunTransitionError(
"Run boundary refresh requires the active writer lease"
)
binding = connection.execute(
"""
SELECT repository_id, checkout_id, target_ref
FROM project_workspace_bindings WHERE project_id = ?
""",
(request["project_id"],),
).fetchone()
if (
binding is None
or binding["repository_id"] != request["repository_id"]
or binding["checkout_id"] != request["checkout_id"]
or binding["target_ref"] != base_ref
):
raise InvalidRunTransitionError(
"Run boundary refresh cannot target an internal checkout"
)
change_set = connection.execute(
"SELECT 1 FROM git_change_sets WHERE run_id = ? LIMIT 1",
(run_id,),
).fetchone()
if change_set is not None:
raise InvalidRunTransitionError(
"Run boundary cannot move after ChangeSet creation"
)
updated = self._rebase_unmaterialized_git_run_in_transaction(
connection,
run_id=run_id,
expected_base_commit=expected_base_commit,
base_commit=base_commit,
base_ref=base_ref,
timestamp=timestamp,
)
self._append_event_in_transaction(
connection,
run_id,
RunEventDraft(
event_id=(
f"workspace.run_base_refreshed:{run_id}:{base_commit}"
),
event_type="workspace.run_base_refreshed",
payload={
"attempt_id": attempt_id,
"request_id": writer_request_id,
"task_id": expected_task_id,
"checkout_id": expected_checkout_id,
"base_ref": base_ref,
"previous_base_commit": expected_base_commit,
"base_commit": base_commit,
"reason": "writer_lease_acquired",
},
created_at=timestamp,
),
expected_project_id=str(request["project_id"]),
)
return updated
def _rebase_unmaterialized_git_run_in_transaction(
self,
connection: sqlite3.Connection,
*,
run_id: str,
expected_base_commit: str | None,
base_commit: str,
base_ref: str,
timestamp: float,
) -> RunGitMaterializationRecord:
run = connection.execute(
"SELECT * FROM git_run_materializations WHERE run_id = ?",
(run_id,),
).fetchone()
if run is None:
raise RunNotFoundError(f"Run {run_id!r} has no Git admission")
if run["materialization_state"] != "unmaterialized":
raise InvalidRunTransitionError(
"only an unmaterialized direct Run can move its base"
)
if run["promoted_commit"] is not None:
raise InvalidRunTransitionError(
"a finalized direct Run cannot move its base"
)
if run["workspace_base_commit"] != expected_base_commit:
raise OptimisticConcurrencyError("direct Run base changed")
managed = connection.execute(
"""
SELECT 1 FROM git_change_sets AS sets
JOIN git_change_set_items AS items
ON items.change_set_id = sets.change_set_id
WHERE sets.run_id = ? LIMIT 1
""",
(run_id,),
).fetchone()
if managed is not None:
raise InvalidRunTransitionError(
"direct Run already contains managed changes"
)
project = connection.execute(
"""
SELECT * FROM git_project_integrations WHERE project_id = ?
""",
(run["project_id"],),
).fetchone()
if project is None:
raise RunJournalError("direct Run has no Project Git state")
next_project_version = int(project["version"]) + 1
connection.execute(
"""
UPDATE git_project_integrations
SET last_synced_user_head = ?, projected_head = ?,
version = ?, updated_at = ?
WHERE project_id = ? AND version = ?
""",
(
base_commit,
base_commit,
next_project_version,
timestamp,
run["project_id"],
int(project["version"]),
),
)
connection.execute(
"""
UPDATE git_run_materializations
SET workspace_base_ref = ?, workspace_base_commit = ?,
project_state_version = ?, version = version + 1,
updated_at = ?
WHERE run_id = ? AND version = ?
""",
(
base_ref,
base_commit,
next_project_version,
timestamp,
run_id,
int(run["version"]),
),
)
updated = connection.execute(
"SELECT * FROM git_run_materializations WHERE run_id = ?",
(run_id,),
).fetchone()
assert updated is not None
return self._run_git_materialization_from_row(updated)
def complete_direct_git_run(
self,
*,
run_id: str,
expected_base_commit: str | None,
terminal_commit: str,
now: float | None = None,
) -> RunGitMaterializationRecord:
"""Persist exact OIDs for a Run executed in its bound checkout."""
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
run = connection.execute(
"SELECT * FROM git_run_materializations WHERE run_id = ?",
(run_id,),
).fetchone()
if run is None:
raise RunNotFoundError(f"Run {run_id!r} has no Git admission")
if run["materialization_state"] != "unmaterialized":
raise InvalidRunTransitionError(
"direct Run was unexpectedly materialized"
)
if run["workspace_base_commit"] != expected_base_commit:
raise OptimisticConcurrencyError("direct Run base changed")
if run["promoted_commit"] is not None:
if run["promoted_commit"] != terminal_commit:
raise IdempotencyConflictError(
"direct Run was finalized at another commit"
)
return self._run_git_materialization_from_row(run)
connection.execute(
"""
UPDATE git_run_materializations
SET promoted_commit = ?, version = version + 1, updated_at = ?
WHERE run_id = ? AND promoted_commit IS NULL
""",
(terminal_commit, timestamp, run_id),
)
connection.execute(
"""
UPDATE git_project_integrations
SET last_synced_user_head = ?, projected_head = ?,
version = version + 1, updated_at = ?
WHERE project_id = ?
""",
(
terminal_commit,
terminal_commit,
timestamp,
run["project_id"],
),
)
updated = connection.execute(
"SELECT * FROM git_run_materializations WHERE run_id = ?",
(run_id,),
).fetchone()
assert updated is not None
return self._run_git_materialization_from_row(updated)
def list_project_git_states(self) -> list[ProjectGitStateRecord]:
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM git_project_integrations
ORDER BY created_at, project_id
"""
).fetchall()
return [self._project_git_state_from_row(row) for row in rows]
def get_run_git_materialization(
self,
run_id: str,
) -> RunGitMaterializationRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
return (
self._run_git_materialization_from_row(row)
if row is not None
else None
)
def list_run_git_materializations(
self,
) -> list[RunGitMaterializationRecord]:
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM git_run_materializations
ORDER BY created_at, run_id
"""
).fetchall()
return [
self._run_git_materialization_from_row(row) for row in rows
]
def claim_git_agent_workspace(
self,
*,
workspace_id: str,
run_id: str,
repository_id: str,
agent_id: str,
agent_ref: str,
worktree_path: str,
base_commit: str,
lease_owner: str,
lease_token: str,
lease_until: float,
now: float | None = None,
) -> GitAgentWorkspaceRecord:
timestamp = now if now is not None else time.time()
if lease_until <= timestamp:
raise ValueError("Agent workspace lease must expire in the future")
identity = (
run_id,
repository_id,
agent_id,
agent_ref,
worktree_path,
base_commit,
)
with self._write_transaction() as connection:
run = connection.execute(
"""
SELECT repository_id, materialization_state
FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
if (
run is None
or run["repository_id"] != repository_id
or run["materialization_state"]
not in {
"materialized",
"promoted",
}
):
raise ValueError("Agent workspace Run is not materialized")
row = connection.execute(
"""
SELECT * FROM git_agent_workspaces
WHERE workspace_id = ? OR (run_id = ? AND agent_id = ?)
""",
(workspace_id, run_id, agent_id),
).fetchone()
if row is None:
connection.execute(
"""
INSERT INTO git_agent_workspaces(
workspace_id, run_id, repository_id, agent_id,
agent_ref, worktree_path, base_commit, head_commit,
state, lease_owner, lease_token, lease_until,
last_operation_id, conflict_interaction_id,
version, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'admitted', ?, ?, ?,
NULL, NULL, 0, ?, ?)
""",
(
workspace_id,
*identity,
base_commit,
lease_owner,
lease_token,
lease_until,
timestamp,
timestamp,
),
)
else:
actual = (
row["run_id"],
row["repository_id"],
row["agent_id"],
row["agent_ref"],
row["worktree_path"],
row["base_commit"],
)
if actual != identity:
raise IdempotencyConflictError(
f"Agent workspace {workspace_id!r} has another owner"
)
if row["state"] == "archived":
raise InvalidRunTransitionError(
f"Agent workspace is {row['state']!r}"
)
if (
row["lease_until"] is not None
and float(row["lease_until"]) > timestamp
and row["lease_owner"] != lease_owner
):
raise OutboxLeaseLostError(
f"Agent workspace {workspace_id!r} is leased"
)
connection.execute(
"""
UPDATE git_agent_workspaces
SET lease_owner = ?, lease_token = ?, lease_until = ?,
version = version + 1, updated_at = ?
WHERE workspace_id = ? AND version = ?
""",
(
lease_owner,
lease_token,
lease_until,
timestamp,
workspace_id,
int(row["version"]),
),
)
claimed = connection.execute(
"SELECT * FROM git_agent_workspaces WHERE workspace_id = ?",
(workspace_id,),
).fetchone()
assert claimed is not None
return self._git_agent_workspace_from_row(claimed)
def transition_git_agent_workspace(
self,
workspace_id: str,
*,
lease_token: str,
expected_state: str,
state: str,
head_commit: str | None = None,
last_operation_id: str | None = None,
conflict_interaction_id: str | None = None,
release_lease: bool = False,
run_event: RunEventDraft | None = None,
now: float | None = None,
) -> GitAgentWorkspaceRecord:
transitions = {
"admitted": {"materializing", "needs_attention"},
"materializing": {"ready", "needs_attention"},
"ready": {"merging", "needs_attention", "archived"},
"merging": {"ready", "merged", "conflicted", "needs_attention"},
"merged": {"ready", "merging", "needs_attention", "archived"},
"conflicted": {
"ready",
"merged",
"needs_attention",
"archived",
},
"needs_attention": {"ready", "archived"},
"archived": set(),
}
if state != expected_state and state not in transitions.get(
expected_state, set()
):
raise InvalidRunTransitionError(
f"Agent workspace cannot transition from {expected_state!r} "
f"to {state!r}"
)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM git_agent_workspaces WHERE workspace_id = ?",
(workspace_id,),
).fetchone()
if row is None:
raise RunNotFoundError(
f"Agent workspace {workspace_id!r} does not exist"
)
if row["lease_token"] != lease_token:
raise OutboxLeaseLostError(
f"Agent workspace {workspace_id!r} lease changed"
)
if row["state"] != expected_state:
raise OptimisticConcurrencyError(
f"Agent workspace expected {expected_state!r}, "
f"found {row['state']!r}"
)
updated = connection.execute(
"""
UPDATE git_agent_workspaces
SET state = ?, head_commit = COALESCE(?, head_commit),
last_operation_id = COALESCE(?, last_operation_id),
conflict_interaction_id = ?,
lease_owner = CASE WHEN ? THEN NULL ELSE lease_owner END,
lease_token = CASE WHEN ? THEN NULL ELSE lease_token END,
lease_until = CASE WHEN ? THEN NULL ELSE lease_until END,
version = version + 1, updated_at = ?
WHERE workspace_id = ? AND version = ? AND state = ?
AND lease_token = ?
""",
(
state,
head_commit,
last_operation_id,
conflict_interaction_id,
release_lease,
release_lease,
release_lease,
timestamp,
workspace_id,
int(row["version"]),
expected_state,
lease_token,
),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
f"Agent workspace {workspace_id!r} changed"
)
if run_event is not None:
self._append_event_in_transaction(
connection,
row["run_id"],
run_event,
)
current = connection.execute(
"SELECT * FROM git_agent_workspaces WHERE workspace_id = ?",
(workspace_id,),
).fetchone()
assert current is not None
return self._git_agent_workspace_from_row(current)
def release_git_agent_workspace_lease(
self,
workspace_id: str,
*,
lease_token: str,
now: float | None = None,
) -> GitAgentWorkspaceRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
updated = connection.execute(
"""
UPDATE git_agent_workspaces
SET lease_owner = NULL, lease_token = NULL,
lease_until = NULL, version = version + 1, updated_at = ?
WHERE workspace_id = ? AND lease_token = ?
""",
(timestamp, workspace_id, lease_token),
)
if updated.rowcount != 1:
raise OutboxLeaseLostError(
f"Agent workspace {workspace_id!r} lease changed"
)
row = connection.execute(
"SELECT * FROM git_agent_workspaces WHERE workspace_id = ?",
(workspace_id,),
).fetchone()
assert row is not None
return self._git_agent_workspace_from_row(row)
def renew_git_agent_workspace_lease(
self,
workspace_id: str,
*,
lease_token: str,
lease_until: float,
now: float | None = None,
) -> GitAgentWorkspaceRecord:
"""Extend an active Agent workspace lease using token CAS."""
timestamp = now if now is not None else time.time()
if lease_until <= timestamp:
raise ValueError("Agent workspace lease must expire in the future")
with self._write_transaction() as connection:
updated = connection.execute(
"""
UPDATE git_agent_workspaces
SET lease_until = ?, version = version + 1, updated_at = ?
WHERE workspace_id = ? AND lease_token = ?
AND lease_until IS NOT NULL AND state != 'archived'
""",
(lease_until, timestamp, workspace_id, lease_token),
)
if updated.rowcount != 1:
raise OutboxLeaseLostError(
f"Agent workspace {workspace_id!r} lease changed"
)
row = connection.execute(
"SELECT * FROM git_agent_workspaces WHERE workspace_id = ?",
(workspace_id,),
).fetchone()
assert row is not None
return self._git_agent_workspace_from_row(row)
def get_git_agent_workspace(
self, run_id: str, agent_id: str
) -> GitAgentWorkspaceRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM git_agent_workspaces
WHERE run_id = ? AND agent_id = ?
""",
(run_id, agent_id),
).fetchone()
return self._git_agent_workspace_from_row(row) if row else None
def list_git_agent_workspaces(
self,
*,
run_id: str | None = None,
states: tuple[str, ...] | None = None,
) -> list[GitAgentWorkspaceRecord]:
clauses: list[str] = []
parameters: list[Any] = []
if run_id is not None:
clauses.append("run_id = ?")
parameters.append(run_id)
if states:
placeholders = ", ".join("?" for _ in states)
clauses.append(f"state IN ({placeholders})")
parameters.extend(states)
query = "SELECT * FROM git_agent_workspaces"
if clauses:
query += " WHERE " + " AND ".join(clauses)
query += " ORDER BY created_at, workspace_id"
with self._lock:
rows = self._connection.execute(query, parameters).fetchall()
return [self._git_agent_workspace_from_row(row) for row in rows]
def reset_git_agent_workspace_leases_after_restart(
self,
*,
now: float | None = None,
) -> int:
"""Release process-local leases before startup reconciliation.
Agent workspace leases protect concurrent operations in one Brain
process. A restarted Brain cannot have a surviving local owner, so
preserving those leases would only delay durable recovery.
"""
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
updated = connection.execute(
"""
UPDATE git_agent_workspaces
SET lease_owner = NULL, lease_token = NULL,
lease_until = NULL, version = version + 1, updated_at = ?
WHERE state != 'archived' AND lease_token IS NOT NULL
""",
(timestamp,),
)
return int(updated.rowcount)
def mark_run_git_attention(
self,
*,
run_id: str,
expected_version: int,
now: float | None = None,
) -> RunGitMaterializationRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"""
SELECT * FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
if row is None:
raise ValueError(f"unknown Run Git state {run_id!r}")
if row["materialization_state"] == "needs_attention":
return self._run_git_materialization_from_row(row)
if int(row["version"]) != expected_version:
raise OptimisticConcurrencyError(
"Run Git state changed before attention marker"
)
if row["materialization_state"] not in {
"materializing",
"materialized",
"promoted",
}:
raise InvalidRunTransitionError(
"Run Git state cannot enter needs_attention from "
f"{row['materialization_state']!r}"
)
connection.execute(
"""
UPDATE git_run_materializations
SET materialization_state = 'needs_attention',
version = version + 1, updated_at = ?
WHERE run_id = ? AND version = ?
""",
(timestamp, run_id, expected_version),
)
row = connection.execute(
"""
SELECT * FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
assert row is not None
return self._run_git_materialization_from_row(row)
def archive_run_git_materialization(
self,
*,
operation_id: str,
run_id: str,
expected_version: int,
expected_run_ref: str,
archive_ref: str,
expected_head: str,
observed_repo_state_digest: str,
now: float | None = None,
) -> RunGitMaterializationRecord:
timestamp = now if now is not None else time.time()
result_json = canonical_json(
{
"run_id": run_id,
"archive_ref": archive_ref,
"commit_oid": expected_head,
}
)
with self._write_transaction() as connection:
operation = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
row = connection.execute(
"SELECT * FROM git_run_materializations WHERE run_id = ?",
(run_id,),
).fetchone()
if operation is None or row is None:
raise ValueError(f"unknown Run Git state {run_id!r}")
if (
operation["repository_id"] != row["repository_id"]
or operation["operation_type"] != "run.archive"
):
raise ValueError("Git archive operation does not own the Run")
if row["materialization_state"] == "archived":
if (
row["run_ref"] != archive_ref
or operation["status"] != "completed"
or operation["result_json"] != result_json
):
raise IdempotencyConflictError(
"Run was archived under another ref"
)
return self._run_git_materialization_from_row(row)
if operation["status"] != "dispatched":
raise InvalidRunTransitionError(
"Run archive operation is not dispatched"
)
if (
int(row["version"]) != expected_version
or row["run_ref"] != expected_run_ref
or row["promoted_commit"] != expected_head
or row["materialization_state"] != "promoted"
):
raise OptimisticConcurrencyError(
"Run Git state changed before archive"
)
connection.execute(
"""
UPDATE git_run_materializations
SET materialization_state = 'archived', run_ref = ?,
worktree_path = NULL, version = version + 1,
updated_at = ?
WHERE run_id = ? AND version = ?
AND materialization_state = 'promoted'
""",
(archive_ref, timestamp, run_id, expected_version),
)
connection.execute(
"""
UPDATE git_operations
SET status = 'completed', result_json = ?,
observed_repo_state_digest = ?, error_code = NULL,
error_message = NULL, updated_at = ?
WHERE operation_id = ? AND status = 'dispatched'
""",
(
result_json,
observed_repo_state_digest,
timestamp,
operation_id,
),
)
row = connection.execute(
"SELECT * FROM git_run_materializations WHERE run_id = ?",
(run_id,),
).fetchone()
assert row is not None
return self._run_git_materialization_from_row(row)
def dispatch_git_run_materialization(
self,
*,
operation_id: str,
run_id: str,
observed_repo_state_digest: str,
now: float | None = None,
) -> RunGitMaterializationRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
operation = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
run = connection.execute(
"""
SELECT * FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
if operation is None or run is None:
raise ValueError("unknown Git materialization operation")
if operation["repository_id"] != run["repository_id"]:
raise ValueError("Git materialization repository mismatch")
if operation["operation_type"] != "run.materialize":
raise ValueError("Git operation is not a Run materialization")
if operation["status"] == "completed":
return self._run_git_materialization_from_row(run)
if operation["status"] == "dispatched":
if run["materialization_state"] != "materializing":
raise RunJournalError(
"dispatched materialization has inconsistent Run state"
)
return self._run_git_materialization_from_row(run)
if operation["status"] != "prepared":
raise InvalidRunTransitionError(
f"materialization cannot dispatch from "
f"{operation['status']!r}"
)
if run["materialization_state"] != "unmaterialized":
raise InvalidRunTransitionError(
f"Run workspace cannot materialize from "
f"{run['materialization_state']!r}"
)
connection.execute(
"""
UPDATE git_operations
SET status = 'dispatched', observed_repo_state_digest = ?,
updated_at = ?
WHERE operation_id = ? AND status = 'prepared'
""",
(observed_repo_state_digest, timestamp, operation_id),
)
connection.execute(
"""
UPDATE git_run_materializations
SET materialization_state = 'materializing',
version = version + 1, updated_at = ?
WHERE run_id = ? AND materialization_state = 'unmaterialized'
""",
(timestamp, run_id),
)
run = connection.execute(
"""
SELECT * FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
assert run is not None
return self._run_git_materialization_from_row(run)
def complete_git_run_materialization(
self,
*,
operation_id: str,
run_id: str,
expected_project_version: int,
expected_project_head: str | None,
project_ref: str,
project_head: str,
project_worktree_path: str,
run_base_ref: str | None,
run_base_commit: str,
run_ref: str,
run_worktree_path: str,
observed_repo_state_digest: str,
now: float | None = None,
) -> tuple[ProjectGitStateRecord, RunGitMaterializationRecord]:
timestamp = now if now is not None else time.time()
result = {
"project_ref": project_ref,
"project_head": project_head,
"project_worktree_path": project_worktree_path,
"run_base_ref": run_base_ref,
"run_base_commit": run_base_commit,
"run_ref": run_ref,
"run_worktree_path": run_worktree_path,
}
result_json = canonical_json(result)
with self._write_transaction() as connection:
operation = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
run = connection.execute(
"""
SELECT * FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
if operation is None or run is None:
raise ValueError("unknown Git materialization operation")
project = connection.execute(
"""
SELECT * FROM git_project_integrations WHERE project_id = ?
""",
(run["project_id"],),
).fetchone()
if project is None:
raise RunJournalError("Run has no Project Git state")
if operation["repository_id"] != run["repository_id"]:
raise ValueError("Git materialization repository mismatch")
if operation["status"] == "completed":
if operation["result_json"] != result_json:
raise IdempotencyConflictError(
"completed Run materialization has another result"
)
return (
self._project_git_state_from_row(project),
self._run_git_materialization_from_row(run),
)
if operation["status"] != "dispatched":
raise InvalidRunTransitionError(
f"materialization cannot complete from "
f"{operation['status']!r}"
)
if run["materialization_state"] != "materializing":
raise InvalidRunTransitionError(
f"Run workspace cannot complete from "
f"{run['materialization_state']!r}"
)
if (
int(project["version"]) != expected_project_version
or project["integration_head"] != expected_project_head
):
raise OptimisticConcurrencyError(
"Project Integration changed during Run materialization"
)
if project["integration_ref"] not in {None, project_ref}:
raise IdempotencyConflictError(
"Project Integration ref conflicts with persisted state"
)
if project["worktree_path"] not in {
None,
project_worktree_path,
}:
raise IdempotencyConflictError(
"Project Integration worktree conflicts with persisted state"
)
if project["integration_ref"] is None:
connection.execute(
"""
UPDATE git_project_integrations
SET integration_ref = ?, integration_head = ?,
worktree_path = ?, projected_head = ?, state = 'ready',
version = version + 1, updated_at = ?
WHERE project_id = ? AND version = ?
""",
(
project_ref,
project_head,
project_worktree_path,
project_head,
timestamp,
run["project_id"],
expected_project_version,
),
)
elif project["integration_head"] != project_head:
raise IdempotencyConflictError(
"Project Integration head conflicts with Git state"
)
connection.execute(
"""
UPDATE git_run_materializations
SET workspace_base_ref = ?, workspace_base_commit = ?,
materialization_state = 'materialized', run_ref = ?,
worktree_path = ?, version = version + 1, updated_at = ?
WHERE run_id = ? AND materialization_state = 'materializing'
""",
(
run_base_ref,
run_base_commit,
run_ref,
run_worktree_path,
timestamp,
run_id,
),
)
connection.execute(
"""
UPDATE git_operations
SET status = 'completed', result_json = ?,
observed_repo_state_digest = ?, error_code = NULL,
error_message = NULL, updated_at = ?
WHERE operation_id = ? AND status = 'dispatched'
""",
(
result_json,
observed_repo_state_digest,
timestamp,
operation_id,
),
)
project = connection.execute(
"""
SELECT * FROM git_project_integrations WHERE project_id = ?
""",
(run["project_id"],),
).fetchone()
run = connection.execute(
"""
SELECT * FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
assert project is not None and run is not None
return (
self._project_git_state_from_row(project),
self._run_git_materialization_from_row(run),
)
def complete_git_run_promotion(
self,
*,
operation_id: str,
run_id: str,
expected_project_version: int,
expected_project_head: str,
promoted_commit: str,
observed_repo_state_digest: str,
now: float | None = None,
) -> tuple[ProjectGitStateRecord, RunGitMaterializationRecord]:
timestamp = now if now is not None else time.time()
result = {
"run_id": run_id,
"expected_project_head": expected_project_head,
"promoted_commit": promoted_commit,
}
result_json = canonical_json(result)
with self._write_transaction() as connection:
operation = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
run = connection.execute(
"""
SELECT * FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
if operation is None or run is None:
raise ValueError("unknown Git promotion operation")
project = connection.execute(
"""
SELECT * FROM git_project_integrations WHERE project_id = ?
""",
(run["project_id"],),
).fetchone()
if project is None:
raise RunJournalError("Run has no Project Git state")
if operation["repository_id"] != run["repository_id"]:
raise ValueError("Git promotion repository mismatch")
if operation["operation_type"] != "run.promote":
raise ValueError("Git operation is not a Run promotion")
if operation["status"] == "completed":
if operation["result_json"] != result_json:
raise IdempotencyConflictError(
"completed Run promotion has another result"
)
return (
self._project_git_state_from_row(project),
self._run_git_materialization_from_row(run),
)
if operation["status"] != "dispatched":
raise InvalidRunTransitionError(
f"promotion cannot complete from {operation['status']!r}"
)
if run["materialization_state"] != "materialized":
raise InvalidRunTransitionError(
f"Run workspace cannot promote from "
f"{run['materialization_state']!r}"
)
if (
int(project["version"]) != expected_project_version
or project["integration_head"] != expected_project_head
):
raise OptimisticConcurrencyError(
"Project Integration changed during Run promotion"
)
if run["workspace_base_commit"] != expected_project_head:
raise OptimisticConcurrencyError(
"Run base is stale and requires merge simulation"
)
if promoted_commit != expected_project_head:
connection.execute(
"""
UPDATE git_project_integrations
SET integration_head = ?, pending_apply = 1,
version = version + 1, updated_at = ?
WHERE project_id = ? AND version = ?
""",
(
promoted_commit,
timestamp,
run["project_id"],
expected_project_version,
),
)
connection.execute(
"""
UPDATE git_run_materializations
SET materialization_state = 'promoted', promoted_commit = ?,
version = version + 1, updated_at = ?
WHERE run_id = ? AND materialization_state = 'materialized'
""",
(promoted_commit, timestamp, run_id),
)
connection.execute(
"""
UPDATE git_operations
SET status = 'completed', result_json = ?,
observed_repo_state_digest = ?, error_code = NULL,
error_message = NULL, updated_at = ?
WHERE operation_id = ? AND status = 'dispatched'
""",
(
result_json,
observed_repo_state_digest,
timestamp,
operation_id,
),
)
project = connection.execute(
"""
SELECT * FROM git_project_integrations WHERE project_id = ?
""",
(run["project_id"],),
).fetchone()
run = connection.execute(
"""
SELECT * FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
assert project is not None and run is not None
return (
self._project_git_state_from_row(project),
self._run_git_materialization_from_row(run),
)
def update_project_git_projection(
self,
*,
project_id: str,
expected_version: int,
expected_integration_head: str,
expected_projected_head: str,
projected_head: str,
now: float | None = None,
) -> ProjectGitStateRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"""
SELECT * FROM git_project_integrations WHERE project_id = ?
""",
(project_id,),
).fetchone()
if row is None:
raise ValueError(f"unknown Project Git state {project_id!r}")
if (
row["integration_head"] == projected_head
and row["projected_head"] == projected_head
):
return self._project_git_state_from_row(row)
if (
int(row["version"]) != expected_version
or row["integration_head"] != expected_integration_head
or row["projected_head"] != expected_projected_head
or projected_head != expected_integration_head
):
raise OptimisticConcurrencyError(
"Project projection state changed concurrently"
)
connection.execute(
"""
UPDATE git_project_integrations
SET projected_head = ?, state = 'ready',
version = version + 1, updated_at = ?
WHERE project_id = ? AND version = ?
""",
(projected_head, timestamp, project_id, expected_version),
)
row = connection.execute(
"""
SELECT * FROM git_project_integrations WHERE project_id = ?
""",
(project_id,),
).fetchone()
assert row is not None
return self._project_git_state_from_row(row)
def complete_project_auto_apply(
self,
*,
operation_id: str,
project_id: str,
expected_version: int,
expected_integration_head: str,
applied_path_sources: dict[str, str],
observed_repo_state_digest: str,
now: float | None = None,
) -> ProjectGitStateRecord:
"""Durably finish one safe Project Integration -> Space projection."""
timestamp = now if now is not None else time.time()
result = {
"project_id": project_id,
"integration_head": expected_integration_head,
"applied_paths": sorted(applied_path_sources),
}
result_json = canonical_json(result)
with self._write_transaction() as connection:
operation = connection.execute(
"SELECT * FROM git_operations WHERE operation_id = ?",
(operation_id,),
).fetchone()
project = connection.execute(
"SELECT * FROM git_project_integrations WHERE project_id = ?",
(project_id,),
).fetchone()
if operation is None or project is None:
raise ValueError("unknown Project auto-apply operation")
if operation["operation_type"] != "project.auto_apply":
raise ValueError("Git operation is not Project auto-apply")
if operation["status"] == "completed":
if operation["result_json"] != result_json:
raise IdempotencyConflictError(
"completed Project auto-apply has another result"
)
return self._project_git_state_from_row(project)
if operation["status"] != "dispatched":
raise InvalidRunTransitionError(
"Project auto-apply must be dispatched before completion"
)
if (
int(project["version"]) != expected_version
or project["integration_head"] != expected_integration_head
or project["projected_head"] != expected_integration_head
):
raise OptimisticConcurrencyError(
"Project changed during automatic Space apply"
)
if set(applied_path_sources.values()) - {
"agent_created",
"agent_modified",
}:
raise ValueError("invalid Project auto-apply path source")
for relative_path, source in applied_path_sources.items():
connection.execute(
"""
INSERT INTO git_managed_paths(
repository_id, relative_path, source,
first_checkpoint_id, created_at, updated_at
) VALUES (?, ?, ?, NULL, ?, ?)
ON CONFLICT(repository_id, relative_path) DO UPDATE SET
updated_at = excluded.updated_at
""",
(
project["repository_id"],
relative_path,
source,
timestamp,
timestamp,
),
)
connection.execute(
"""
UPDATE git_project_integrations
SET pending_apply = 0, state = 'ready', version = version + 1,
updated_at = ?
WHERE project_id = ? AND version = ?
""",
(timestamp, project_id, expected_version),
)
connection.execute(
"""
UPDATE git_operations
SET status = 'completed', result_json = ?,
observed_repo_state_digest = ?, error_code = NULL,
error_message = NULL, updated_at = ?
WHERE operation_id = ? AND status = 'dispatched'
""",
(
result_json,
observed_repo_state_digest,
timestamp,
operation_id,
),
)
project = connection.execute(
"SELECT * FROM git_project_integrations WHERE project_id = ?",
(project_id,),
).fetchone()
assert project is not None
return self._project_git_state_from_row(project)
def mark_project_git_attention(
self,
*,
project_id: str,
expected_version: int,
now: float | None = None,
) -> ProjectGitStateRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"""
SELECT * FROM git_project_integrations WHERE project_id = ?
""",
(project_id,),
).fetchone()
if row is None:
raise ValueError(f"unknown Project Git state {project_id!r}")
if row["state"] == "needs_attention":
return self._project_git_state_from_row(row)
if int(row["version"]) != expected_version:
raise OptimisticConcurrencyError(
"Project Git state changed before attention marker"
)
connection.execute(
"""
UPDATE git_project_integrations
SET state = 'needs_attention', version = version + 1,
updated_at = ?
WHERE project_id = ? AND version = ?
""",
(timestamp, project_id, expected_version),
)
row = connection.execute(
"""
SELECT * FROM git_project_integrations WHERE project_id = ?
""",
(project_id,),
).fetchone()
assert row is not None
return self._project_git_state_from_row(row)
def create_workspace_read_snapshot(
self,
*,
snapshot_id: str,
run_id: str,
project_id: str,
repository_id: str,
project_base_commit: str | None,
common_base_commit: str | None,
project_state_version: int,
snapshot_ref: str | None,
user_head: str | None,
user_working_state_digest: str,
expires_at: float | None = None,
now: float | None = None,
) -> WorkspaceReadSnapshotRecord:
"""Create the first lazy read snapshot, or return the active one.
The transaction is intentionally metadata-only. File bytes and Git
objects stay outside SQLite and are addressed by digest/ref.
"""
if not self._is_sha256(user_working_state_digest):
raise ValueError("working-state digest must be SHA-256")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
active = connection.execute(
"""
SELECT * FROM workspace_read_snapshots
WHERE run_id = ? AND state = 'active'
ORDER BY generation DESC
LIMIT 1
""",
(run_id,),
).fetchone()
if active is not None:
return self._workspace_read_snapshot_from_row(active)
run = connection.execute(
"""
SELECT * FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
if run is None:
raise ValueError(f"Run {run_id!r} has no Git admission")
if (
run["project_id"] != project_id
or run["repository_id"] != repository_id
):
raise IdempotencyConflictError(
f"Run {run_id!r} has another snapshot owner"
)
generation_row = connection.execute(
"""
SELECT COALESCE(MAX(generation), -1) + 1 AS generation
FROM workspace_read_snapshots WHERE run_id = ?
""",
(run_id,),
).fetchone()
assert generation_row is not None
generation = int(generation_row["generation"])
empty_manifest_digest = canonical_digest([])
connection.execute(
"""
INSERT INTO workspace_read_snapshots(
snapshot_id, run_id, project_id, repository_id,
generation, project_base_commit, common_base_commit,
project_state_version, snapshot_ref, user_head,
user_working_state_digest, overlay_manifest_digest,
state, expires_at, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active',
?, ?, ?)
""",
(
snapshot_id,
run_id,
project_id,
repository_id,
generation,
project_base_commit,
common_base_commit,
project_state_version,
snapshot_ref,
user_head,
user_working_state_digest,
empty_manifest_digest,
expires_at,
timestamp,
timestamp,
),
)
row = connection.execute(
"""
SELECT * FROM workspace_read_snapshots
WHERE snapshot_id = ?
""",
(snapshot_id,),
).fetchone()
assert row is not None
return self._workspace_read_snapshot_from_row(row)
def get_active_workspace_read_snapshot(
self,
run_id: str,
) -> WorkspaceReadSnapshotRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_read_snapshots
WHERE run_id = ? AND state = 'active'
ORDER BY generation DESC
LIMIT 1
""",
(run_id,),
).fetchone()
return (
self._workspace_read_snapshot_from_row(row)
if row is not None
else None
)
def get_workspace_read_snapshot(
self,
snapshot_id: str,
) -> WorkspaceReadSnapshotRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_read_snapshots
WHERE snapshot_id = ?
""",
(snapshot_id,),
).fetchone()
return (
self._workspace_read_snapshot_from_row(row)
if row is not None
else None
)
def replace_active_workspace_read_snapshot(
self,
run_id: str,
*,
now: float | None = None,
) -> None:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
connection.execute(
"""
UPDATE workspace_read_snapshots
SET state = 'stale', updated_at = ?
WHERE run_id = ? AND state = 'active'
""",
(timestamp, run_id),
)
def get_workspace_overlay_entry(
self,
snapshot_id: str,
relative_path: str,
) -> WorkspaceOverlayEntryRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_overlay_entries
WHERE snapshot_id = ? AND relative_path = ?
""",
(snapshot_id, relative_path),
).fetchone()
return (
self._workspace_overlay_entry_from_row(row)
if row is not None
else None
)
def list_workspace_overlay_entries(
self,
snapshot_id: str,
) -> list[WorkspaceOverlayEntryRecord]:
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM workspace_overlay_entries
WHERE snapshot_id = ?
ORDER BY relative_path
""",
(snapshot_id,),
).fetchall()
return [
self._workspace_overlay_entry_from_row(row) for row in rows
]
def put_workspace_overlay_entry(
self,
*,
snapshot_id: str,
relative_path: str,
source_kind: str,
entry_state: str,
source_token: dict[str, Any],
project_blob_oid: str | None,
size_bytes: int,
now: float | None = None,
) -> WorkspaceOverlayEntryRecord:
timestamp = now if now is not None else time.time()
source_token_json = canonical_json(source_token)
expected = (
source_kind,
entry_state,
source_token_json,
project_blob_oid,
size_bytes,
)
with self._write_transaction() as connection:
snapshot = connection.execute(
"""
SELECT state FROM workspace_read_snapshots
WHERE snapshot_id = ?
""",
(snapshot_id,),
).fetchone()
if snapshot is None:
raise ValueError(f"unknown snapshot {snapshot_id!r}")
if snapshot["state"] != "active":
raise InvalidRunTransitionError(
f"snapshot {snapshot_id!r} is not active"
)
row = connection.execute(
"""
SELECT * FROM workspace_overlay_entries
WHERE snapshot_id = ? AND relative_path = ?
""",
(snapshot_id, relative_path),
).fetchone()
if row is not None:
actual = (
row["source_kind"],
row["entry_state"],
row["source_token_json"],
row["project_blob_oid"],
int(row["size_bytes"]),
)
if actual != expected:
raise IdempotencyConflictError(
f"snapshot path {relative_path!r} changed after pin"
)
return self._workspace_overlay_entry_from_row(row)
connection.execute(
"""
INSERT INTO workspace_overlay_entries(
snapshot_id, relative_path, source_kind, entry_state,
source_token_json, project_blob_oid, size_bytes,
created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
snapshot_id,
relative_path,
*expected,
timestamp,
timestamp,
),
)
manifest_rows = connection.execute(
"""
SELECT relative_path, source_kind, entry_state,
source_token_json, project_blob_oid, size_bytes
FROM workspace_overlay_entries
WHERE snapshot_id = ?
ORDER BY relative_path
""",
(snapshot_id,),
).fetchall()
manifest = [
{
"relative_path": item["relative_path"],
"source_kind": item["source_kind"],
"entry_state": item["entry_state"],
"source_token": json.loads(item["source_token_json"]),
"project_blob_oid": item["project_blob_oid"],
"size_bytes": int(item["size_bytes"]),
}
for item in manifest_rows
]
connection.execute(
"""
UPDATE workspace_read_snapshots
SET overlay_manifest_digest = ?, updated_at = ?
WHERE snapshot_id = ? AND state = 'active'
""",
(canonical_digest(manifest), timestamp, snapshot_id),
)
row = connection.execute(
"""
SELECT * FROM workspace_overlay_entries
WHERE snapshot_id = ? AND relative_path = ?
""",
(snapshot_id, relative_path),
).fetchone()
assert row is not None
return self._workspace_overlay_entry_from_row(row)
def record_workspace_snapshot_range(
self,
*,
snapshot_id: str,
relative_path: str,
start_offset: int,
end_offset: int,
content_digest: str,
cache_key: str,
now: float | None = None,
) -> WorkspaceSnapshotRangeRecord:
if start_offset < 0 or end_offset < start_offset:
raise ValueError("invalid snapshot byte range")
if not self._is_sha256(content_digest) or not self._is_sha256(
cache_key
):
raise ValueError("snapshot range digests must be SHA-256")
timestamp = now if now is not None else time.time()
expected = (content_digest, cache_key)
with self._write_transaction() as connection:
row = connection.execute(
"""
SELECT * FROM workspace_snapshot_ranges
WHERE snapshot_id = ? AND relative_path = ?
AND start_offset = ? AND end_offset = ?
""",
(snapshot_id, relative_path, start_offset, end_offset),
).fetchone()
if row is not None:
actual = (row["content_digest"], row["cache_key"])
if actual != expected:
raise IdempotencyConflictError(
"snapshot range content changed after pin"
)
return self._workspace_snapshot_range_from_row(row)
connection.execute(
"""
INSERT INTO workspace_snapshot_ranges(
snapshot_id, relative_path, start_offset, end_offset,
content_digest, cache_key, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(
snapshot_id,
relative_path,
start_offset,
end_offset,
content_digest,
cache_key,
timestamp,
),
)
row = connection.execute(
"""
SELECT * FROM workspace_snapshot_ranges
WHERE snapshot_id = ? AND relative_path = ?
AND start_offset = ? AND end_offset = ?
""",
(snapshot_id, relative_path, start_offset, end_offset),
).fetchone()
assert row is not None
return self._workspace_snapshot_range_from_row(row)
def get_workspace_snapshot_range(
self,
*,
snapshot_id: str,
relative_path: str,
start_offset: int,
end_offset: int,
) -> WorkspaceSnapshotRangeRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_snapshot_ranges
WHERE snapshot_id = ? AND relative_path = ?
AND start_offset = ? AND end_offset = ?
""",
(snapshot_id, relative_path, start_offset, end_offset),
).fetchone()
return (
self._workspace_snapshot_range_from_row(row)
if row is not None
else None
)
def update_workspace_overlay_entry_state(
self,
*,
snapshot_id: str,
relative_path: str,
expected_state: str,
state: str,
now: float | None = None,
) -> WorkspaceOverlayEntryRecord:
allowed = {
"read_only": {"imported_preimage", "conflicted"},
"imported_preimage": {"agent_modified", "conflicted"},
"agent_modified": set(),
"conflicted": set(),
}
if state != expected_state and state not in allowed.get(
expected_state, set()
):
raise InvalidRunTransitionError(
f"overlay entry cannot transition from {expected_state!r} "
f"to {state!r}"
)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"""
SELECT * FROM workspace_overlay_entries
WHERE snapshot_id = ? AND relative_path = ?
""",
(snapshot_id, relative_path),
).fetchone()
if row is None:
raise ValueError(f"unknown overlay path {relative_path!r}")
if row["entry_state"] == state:
return self._workspace_overlay_entry_from_row(row)
if row["entry_state"] != expected_state:
raise OptimisticConcurrencyError(
f"overlay path {relative_path!r} changed concurrently"
)
connection.execute(
"""
UPDATE workspace_overlay_entries
SET entry_state = ?, updated_at = ?
WHERE snapshot_id = ? AND relative_path = ?
AND entry_state = ?
""",
(
state,
timestamp,
snapshot_id,
relative_path,
expected_state,
),
)
row = connection.execute(
"""
SELECT * FROM workspace_overlay_entries
WHERE snapshot_id = ? AND relative_path = ?
""",
(snapshot_id, relative_path),
).fetchone()
assert row is not None
return self._workspace_overlay_entry_from_row(row)
def complete_workspace_overlay_materialization(
self,
*,
snapshot_id: str,
relative_path: str,
content_digest: str,
preimage_cache_key: str,
now: float | None = None,
) -> WorkspaceOverlayEntryRecord:
if not self._is_sha256(content_digest) or not self._is_sha256(
preimage_cache_key
):
raise ValueError("overlay materialization digests must be SHA-256")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"""
SELECT * FROM workspace_overlay_entries
WHERE snapshot_id = ? AND relative_path = ?
""",
(snapshot_id, relative_path),
).fetchone()
if row is None or row["source_kind"] != "user_overlay":
raise ValueError("unknown User overlay entry")
if row["entry_state"] == "imported_preimage":
if (
row["materialized_content_digest"] != content_digest
or row["preimage_cache_key"] != preimage_cache_key
):
raise IdempotencyConflictError(
f"overlay path {relative_path!r} has another preimage"
)
return self._workspace_overlay_entry_from_row(row)
if row["entry_state"] != "read_only":
raise InvalidRunTransitionError(
f"overlay path {relative_path!r} cannot materialize from "
f"{row['entry_state']!r}"
)
connection.execute(
"""
UPDATE workspace_overlay_entries
SET entry_state = 'imported_preimage',
materialized_content_digest = ?, preimage_cache_key = ?,
updated_at = ?
WHERE snapshot_id = ? AND relative_path = ?
AND entry_state = 'read_only'
""",
(
content_digest,
preimage_cache_key,
timestamp,
snapshot_id,
relative_path,
),
)
row = connection.execute(
"""
SELECT * FROM workspace_overlay_entries
WHERE snapshot_id = ? AND relative_path = ?
""",
(snapshot_id, relative_path),
).fetchone()
assert row is not None
return self._workspace_overlay_entry_from_row(row)
def ensure_git_change_set(
self,
*,
change_set_id: str,
run_id: str,
repository_id: str,
worktree_ref: str,
base_commit: str | None,
now: float | None = None,
) -> GitChangeSetRecord:
timestamp = now if now is not None else time.time()
expected = (run_id, repository_id, worktree_ref, base_commit)
with self._write_transaction() as connection:
run = connection.execute(
"""
SELECT repository_id, run_ref, workspace_base_commit
FROM git_run_materializations WHERE run_id = ?
""",
(run_id,),
).fetchone()
if run is None or run["repository_id"] != repository_id:
raise ValueError("ChangeSet Run/repository mismatch")
if run["run_ref"] not in {None, worktree_ref}:
agent_owner = connection.execute(
"""
SELECT 1 FROM git_agent_workspaces
WHERE run_id = ? AND agent_ref = ?
""",
(run_id, worktree_ref),
).fetchone()
if agent_owner is None:
raise IdempotencyConflictError(
"ChangeSet worktree ref does not belong to the Run"
)
row = connection.execute(
"""
SELECT * FROM git_change_sets
WHERE change_set_id = ? OR (
run_id = ? AND worktree_ref = ?
)
""",
(change_set_id, run_id, worktree_ref),
).fetchone()
if row is not None:
actual = (
row["run_id"],
row["repository_id"],
row["worktree_ref"],
row["base_commit"],
)
if actual != expected:
raise IdempotencyConflictError(
f"ChangeSet {change_set_id!r} has another owner"
)
return self._git_change_set_from_row(row)
connection.execute(
"""
INSERT INTO git_change_sets(
change_set_id, run_id, repository_id, worktree_ref,
base_commit, state, version, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, 'open', 0, ?, ?)
""",
(change_set_id, *expected, timestamp, timestamp),
)
row = connection.execute(
"""
SELECT * FROM git_change_sets WHERE change_set_id = ?
""",
(change_set_id,),
).fetchone()
assert row is not None
return self._git_change_set_from_row(row)
def get_git_change_set_for_run(
self,
run_id: str,
) -> GitChangeSetRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM git_change_sets
WHERE run_id = ?
ORDER BY created_at DESC
LIMIT 1
""",
(run_id,),
).fetchone()
return self._git_change_set_from_row(row) if row else None
def list_git_change_sets(
self,
*,
states: tuple[str, ...] | None = None,
) -> list[GitChangeSetRecord]:
with self._lock:
query = "SELECT * FROM git_change_sets"
parameters: list[Any] = []
if states:
placeholders = ", ".join("?" for _ in states)
query += f" WHERE state IN ({placeholders})"
parameters.extend(states)
query += " ORDER BY created_at, change_set_id"
rows = self._connection.execute(query, parameters).fetchall()
return [self._git_change_set_from_row(row) for row in rows]
def update_git_change_set_state(
self,
*,
change_set_id: str,
expected_state: str,
state: str,
now: float | None = None,
) -> GitChangeSetRecord:
transitions = {
"open": {"checkpointed", "discarded", "needs_attention"},
"checkpointed": set(),
"discarded": set(),
"needs_attention": {"open", "discarded"},
}
if state != expected_state and state not in transitions.get(
expected_state, set()
):
raise InvalidRunTransitionError(
f"ChangeSet cannot transition from {expected_state!r} "
f"to {state!r}"
)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM git_change_sets WHERE change_set_id = ?",
(change_set_id,),
).fetchone()
if row is None:
raise ValueError(f"unknown ChangeSet {change_set_id!r}")
if row["state"] == state:
return self._git_change_set_from_row(row)
if row["state"] != expected_state:
raise OptimisticConcurrencyError(
f"ChangeSet {change_set_id!r} changed concurrently"
)
connection.execute(
"""
UPDATE git_change_sets
SET state = ?, version = version + 1, updated_at = ?
WHERE change_set_id = ? AND state = ?
""",
(state, timestamp, change_set_id, expected_state),
)
row = connection.execute(
"SELECT * FROM git_change_sets WHERE change_set_id = ?",
(change_set_id,),
).fetchone()
assert row is not None
return self._git_change_set_from_row(row)
def put_git_change_set_item(
self,
*,
change_set_id: str,
relative_path: str,
operation_request_id: str,
actor_id: str,
trigger: str,
change_kind: str,
source: str,
preimage_digest: str | None,
result_digest: str | None,
size_bytes: int | None,
now: float | None = None,
) -> GitChangeSetItemRecord:
if not operation_request_id or not actor_id or not trigger:
raise ValueError(
"operation_request_id, actor_id, and trigger must not be empty"
)
if change_kind not in {"added", "modified", "deleted", "renamed"}:
raise ValueError(f"unsupported change kind {change_kind!r}")
if source not in {
"agent_created",
"agent_modified",
"user_selected",
"overlay_preimage",
"artifact_event",
"worktree_delta",
}:
raise ValueError(f"unsupported ChangeSet source {source!r}")
for digest in (preimage_digest, result_digest):
if digest is not None and not self._is_sha256(digest):
raise ValueError("ChangeSet digests must be SHA-256")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
change_set = connection.execute(
"""
SELECT state FROM git_change_sets WHERE change_set_id = ?
""",
(change_set_id,),
).fetchone()
if change_set is None:
raise ValueError(f"unknown ChangeSet {change_set_id!r}")
if change_set["state"] != "open":
raise InvalidRunTransitionError(
f"ChangeSet {change_set_id!r} is not open"
)
row = connection.execute(
"""
SELECT * FROM git_change_set_items
WHERE change_set_id = ? AND relative_path = ?
""",
(change_set_id, relative_path),
).fetchone()
if row is None:
connection.execute(
"""
INSERT INTO git_change_set_items(
change_set_id, relative_path, operation_request_id,
actor_id, trigger, change_kind, source,
preimage_digest, result_digest, size_bytes,
item_state, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?)
""",
(
change_set_id,
relative_path,
operation_request_id,
actor_id,
trigger,
change_kind,
source,
preimage_digest,
result_digest,
size_bytes,
timestamp,
timestamp,
),
)
else:
canonical = (
row["change_kind"] == change_kind
and row["operation_request_id"] == operation_request_id
and row["actor_id"] == actor_id
and row["trigger"] == trigger
and row["source"] == source
and row["preimage_digest"] == preimage_digest
and row["result_digest"] == result_digest
and row["size_bytes"] == size_bytes
)
if canonical:
return self._git_change_set_item_from_row(row)
if row["item_state"] in {
"pending",
"preimage_checkpointed",
}:
raise InvalidRunTransitionError(
f"ChangeSet path {relative_path!r} has an unfinished "
"operation"
)
connection.execute(
"""
UPDATE git_change_set_items
SET operation_request_id = ?, actor_id = ?, trigger = ?,
change_kind = ?, source = ?, preimage_digest = ?,
result_digest = ?, size_bytes = ?,
item_state = 'pending', updated_at = ?
WHERE change_set_id = ? AND relative_path = ?
AND item_state IN ('checkpointed', 'ignored')
""",
(
operation_request_id,
actor_id,
trigger,
change_kind,
source,
preimage_digest,
result_digest,
size_bytes,
timestamp,
change_set_id,
relative_path,
),
)
connection.execute(
"""
UPDATE git_change_sets
SET version = version + 1, updated_at = ?
WHERE change_set_id = ?
""",
(timestamp, change_set_id),
)
row = connection.execute(
"""
SELECT * FROM git_change_set_items
WHERE change_set_id = ? AND relative_path = ?
""",
(change_set_id, relative_path),
).fetchone()
assert row is not None
return self._git_change_set_item_from_row(row)
def ensure_git_mutation_intent(
self,
*,
intent_id: str,
change_set_id: str,
operation_request_id: str,
mutation_scope: str,
relative_path: str | None,
preimage_digest: str | None,
actor_id: str,
trigger: str,
now: float | None = None,
) -> GitMutationIntentRecord:
if mutation_scope not in {"exact_path", "broad_process"}:
raise ValueError(f"unsupported mutation scope {mutation_scope!r}")
if (mutation_scope == "exact_path") != (relative_path is not None):
raise ValueError("exact-path mutation intents must name one path")
if preimage_digest is not None and not self._is_sha256(
preimage_digest
):
raise ValueError("mutation preimage digest must be SHA-256")
if not operation_request_id or not actor_id or not trigger:
raise ValueError("mutation intent identity must not be empty")
timestamp = now if now is not None else time.time()
expected = (
change_set_id,
operation_request_id,
mutation_scope,
relative_path,
preimage_digest,
actor_id,
trigger,
)
with self._write_transaction() as connection:
row = connection.execute(
"""
SELECT * FROM git_mutation_intents
WHERE intent_id = ? OR (
change_set_id = ? AND operation_request_id = ?
)
""",
(intent_id, change_set_id, operation_request_id),
).fetchone()
if row is not None:
actual = (
row["change_set_id"],
row["operation_request_id"],
row["mutation_scope"],
row["relative_path"],
row["preimage_digest"],
row["actor_id"],
row["trigger"],
)
if actual != expected:
raise IdempotencyConflictError(
f"mutation intent {intent_id!r} was reused"
)
return self._git_mutation_intent_from_row(row)
connection.execute(
"""
INSERT INTO git_mutation_intents(
intent_id, change_set_id, operation_request_id,
mutation_scope, relative_path, preimage_digest,
actor_id, trigger, status, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'prepared', ?, ?)
""",
(intent_id, *expected, timestamp, timestamp),
)
row = connection.execute(
"SELECT * FROM git_mutation_intents WHERE intent_id = ?",
(intent_id,),
).fetchone()
assert row is not None
return self._git_mutation_intent_from_row(row)
def list_git_mutation_intents(
self,
*,
statuses: tuple[str, ...] | None = None,
) -> list[GitMutationIntentRecord]:
with self._lock:
query = "SELECT * FROM git_mutation_intents"
parameters: list[Any] = []
if statuses:
placeholders = ", ".join("?" for _ in statuses)
query += f" WHERE status IN ({placeholders})"
parameters.extend(statuses)
query += " ORDER BY created_at, intent_id"
rows = self._connection.execute(query, parameters).fetchall()
return [self._git_mutation_intent_from_row(row) for row in rows]
def update_git_mutation_intent_status(
self,
*,
intent_id: str,
expected_status: str,
status: str,
now: float | None = None,
) -> GitMutationIntentRecord:
transitions = {
"prepared": {"completed", "needs_attention"},
"completed": set(),
"needs_attention": {"prepared"},
}
if status != expected_status and status not in transitions.get(
expected_status, set()
):
raise InvalidRunTransitionError(
f"mutation intent cannot transition from {expected_status!r} "
f"to {status!r}"
)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM git_mutation_intents WHERE intent_id = ?",
(intent_id,),
).fetchone()
if row is None:
raise ValueError(f"unknown mutation intent {intent_id!r}")
if row["status"] == status:
return self._git_mutation_intent_from_row(row)
if row["status"] != expected_status:
raise OptimisticConcurrencyError(
f"mutation intent {intent_id!r} changed concurrently"
)
connection.execute(
"""
UPDATE git_mutation_intents
SET status = ?, updated_at = ?
WHERE intent_id = ? AND status = ?
""",
(status, timestamp, intent_id, expected_status),
)
row = connection.execute(
"SELECT * FROM git_mutation_intents WHERE intent_id = ?",
(intent_id,),
).fetchone()
assert row is not None
return self._git_mutation_intent_from_row(row)
def list_git_change_set_items(
self,
change_set_id: str,
*,
states: tuple[str, ...] | None = None,
) -> list[GitChangeSetItemRecord]:
with self._lock:
query = (
"SELECT * FROM git_change_set_items WHERE change_set_id = ?"
)
parameters: list[Any] = [change_set_id]
if states:
placeholders = ", ".join("?" for _ in states)
query += f" AND item_state IN ({placeholders})"
parameters.extend(states)
query += " ORDER BY relative_path"
rows = self._connection.execute(query, parameters).fetchall()
return [self._git_change_set_item_from_row(row) for row in rows]
def update_git_change_set_item_state(
self,
*,
change_set_id: str,
relative_path: str,
expected_state: str,
state: str,
now: float | None = None,
) -> GitChangeSetItemRecord:
transitions = {
"pending": {"preimage_checkpointed", "checkpointed", "ignored"},
"preimage_checkpointed": {"checkpointed"},
"checkpointed": set(),
"ignored": set(),
}
if state != expected_state and state not in transitions.get(
expected_state, set()
):
raise InvalidRunTransitionError(
f"ChangeSet item cannot transition from {expected_state!r} "
f"to {state!r}"
)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"""
SELECT * FROM git_change_set_items
WHERE change_set_id = ? AND relative_path = ?
""",
(change_set_id, relative_path),
).fetchone()
if row is None:
raise ValueError(f"unknown ChangeSet path {relative_path!r}")
if row["item_state"] == state:
return self._git_change_set_item_from_row(row)
if row["item_state"] != expected_state:
raise OptimisticConcurrencyError(
f"ChangeSet path {relative_path!r} changed concurrently"
)
connection.execute(
"""
UPDATE git_change_set_items
SET item_state = ?, updated_at = ?
WHERE change_set_id = ? AND relative_path = ?
AND item_state = ?
""",
(
state,
timestamp,
change_set_id,
relative_path,
expected_state,
),
)
connection.execute(
"""
UPDATE git_change_sets
SET version = version + 1, updated_at = ?
WHERE change_set_id = ?
""",
(timestamp, change_set_id),
)
row = connection.execute(
"""
SELECT * FROM git_change_set_items
WHERE change_set_id = ? AND relative_path = ?
""",
(change_set_id, relative_path),
).fetchone()
assert row is not None
return self._git_change_set_item_from_row(row)
def ensure_run(
self,
*,
run_id: str,
project_id: str,
status: str = "running",
timeout_policy_version: str = "v1",
deadline_at: float | None = None,
now: float | None = None,
) -> RunRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
connection.execute(
"""
INSERT OR IGNORE INTO runs(
run_id, project_id, status, version, active_attempt_id,
deadline_at, timeout_policy_version, created_at, updated_at
) VALUES (?, ?, ?, 0, NULL, ?, ?, ?, ?)
""",
(
run_id,
project_id,
status,
deadline_at,
timeout_policy_version,
timestamp,
timestamp,
),
)
row = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
assert row is not None
if row["project_id"] != project_id:
raise IdempotencyConflictError(
f"run_id {run_id!r} already belongs to another project"
)
if row["timeout_policy_version"] != timeout_policy_version:
raise IdempotencyConflictError(
f"run_id {run_id!r} was reused with a different timeout policy"
)
persisted_deadline = (
float(row["deadline_at"])
if row["deadline_at"] is not None
else None
)
if persisted_deadline != deadline_at:
raise IdempotencyConflictError(
f"run_id {run_id!r} was reused with a different deadline"
)
return self._run_from_row(row)
def get_run(self, run_id: str) -> RunRecord | None:
with self._lock:
row = self._connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
return self._run_from_row(row) if row is not None else None
def start_model_invocation(
self,
*,
invocation_id: str,
run_id: str,
attempt_id: str | None,
agent_id: str,
logical_call_id: str,
provider: str,
model: str,
transport: str,
thinking_effort: str | None,
request: dict[str, Any],
redaction_version: str = "model-invocation-v1",
now: float | None = None,
) -> ModelInvocationRecord:
"""Persist the redacted CAMEL model-boundary request before dispatch.
The caller may retry a logical model turn. SQLite allocates the
retry index while holding the writer lock so concurrent calls cannot
claim the same trajectory position.
"""
if not invocation_id.strip() or not logical_call_id.strip():
raise ValueError("model invocation ids are required")
if not agent_id.strip() or not provider.strip() or not model.strip():
raise ValueError("model invocation identity is incomplete")
timestamp = now if now is not None else time.time()
safe_request, request_json = _project_model_document(
"request", request
)
request_digest = hashlib.sha256(
request_json.encode("utf-8")
).hexdigest()
with self._write_transaction() as connection:
duplicate = connection.execute(
"SELECT * FROM model_invocations WHERE invocation_id = ?",
(invocation_id,),
).fetchone()
if duplicate is not None:
if (
duplicate["run_id"] != run_id
or duplicate["logical_call_id"] != logical_call_id
or duplicate["request_digest"] != request_digest
):
raise IdempotencyConflictError(
"model invocation id was reused with different input"
)
return self._model_invocation_from_row(duplicate)
run = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
if run is None:
raise RunNotFoundError(f"run_id {run_id!r} does not exist")
if attempt_id is not None:
attempt = connection.execute(
"SELECT run_id FROM run_attempts WHERE attempt_id = ?",
(attempt_id,),
).fetchone()
if attempt is None or attempt["run_id"] != run_id:
raise IdempotencyConflictError(
"model invocation attempt does not belong to the Run"
)
retry_index = int(
connection.execute(
"""
SELECT COALESCE(MAX(retry_index), -1) + 1
FROM model_invocations WHERE logical_call_id = ?
""",
(logical_call_id,),
).fetchone()[0]
)
connection.execute(
"""
INSERT INTO model_invocations(
invocation_id, run_id, attempt_id, agent_id,
logical_call_id, retry_index, status, provider, model,
transport, thinking_effort, request_json, response_json,
request_digest, response_digest, prompt_tokens,
completion_tokens, cache_read_tokens, cache_write_tokens,
finish_reason, error_code, error_message,
redaction_version, started_at, first_token_at, completed_at
) VALUES (
?, ?, ?, ?, ?, ?, 'dispatched', ?, ?, ?, ?, ?, NULL,
?, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
?, ?, NULL, NULL
)
""",
(
invocation_id,
run_id,
attempt_id,
agent_id,
logical_call_id,
retry_index,
provider,
model,
transport,
thinking_effort,
request_json,
request_digest,
redaction_version,
timestamp,
),
)
event_payload = {
"invocation_id": invocation_id,
"attempt_id": attempt_id,
"agent_id": agent_id,
"logical_call_id": logical_call_id,
"retry_index": retry_index,
"provider": provider,
"model": model,
"transport": transport,
"request_digest": request_digest,
"redaction_version": redaction_version,
}
self._insert_model_invocation_event(
connection,
invocation_id=invocation_id,
event_type="dispatched",
payload=event_payload,
created_at=timestamp,
)
self._append_event_in_transaction(
connection,
run_id,
RunEventDraft(
event_id=f"model-invocation:{invocation_id}:dispatched",
event_type="model.invocation.dispatched",
payload=event_payload,
created_at=timestamp,
),
)
row = connection.execute(
"SELECT * FROM model_invocations WHERE invocation_id = ?",
(invocation_id,),
).fetchone()
assert row is not None
return self._model_invocation_from_row(row)
def mark_model_invocation_first_token(
self,
invocation_id: str,
*,
now: float | None = None,
) -> ModelInvocationRecord:
"""Persist one latency marker; token deltas are never journaled."""
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM model_invocations WHERE invocation_id = ?",
(invocation_id,),
).fetchone()
if row is None:
raise RunJournalError("model invocation does not exist")
if row["first_token_at"] is None:
connection.execute(
"""
UPDATE model_invocations SET first_token_at = ?
WHERE invocation_id = ? AND first_token_at IS NULL
""",
(timestamp, invocation_id),
)
self._insert_model_invocation_event(
connection,
invocation_id=invocation_id,
event_type="first_token",
payload={"invocation_id": invocation_id},
created_at=timestamp,
)
updated = connection.execute(
"SELECT * FROM model_invocations WHERE invocation_id = ?",
(invocation_id,),
).fetchone()
assert updated is not None
return self._model_invocation_from_row(updated)
def finish_model_invocation(
self,
invocation_id: str,
*,
status: str,
response: dict[str, Any] | None = None,
prompt_tokens: int | None = None,
completion_tokens: int | None = None,
cache_read_tokens: int | None = None,
cache_write_tokens: int | None = None,
finish_reason: str | None = None,
error_code: str | None = None,
error_message: str | None = None,
now: float | None = None,
) -> ModelInvocationRecord:
"""Close one provider call and append its lightweight Run event."""
if status not in {"completed", "failed", "outcome_unknown"}:
raise ValueError("invalid terminal model invocation status")
timestamp = now if now is not None else time.time()
safe_response: dict[str, Any] | None = None
response_json: str | None = None
response_digest: str | None = None
if response is not None:
safe_response, response_json = _project_model_document(
"response", response
)
response_digest = hashlib.sha256(
response_json.encode("utf-8")
).hexdigest()
safe_error = None
if error_message:
from app.permission_policy.models import redact_action_arguments
safe_error = str(
redact_action_arguments({"message": error_message})["message"]
)[:16_384]
for name, value in {
"prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens,
"cache_read_tokens": cache_read_tokens,
"cache_write_tokens": cache_write_tokens,
}.items():
if value is not None and value < 0:
raise ValueError(f"{name} cannot be negative")
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM model_invocations WHERE invocation_id = ?",
(invocation_id,),
).fetchone()
if row is None:
raise RunJournalError("model invocation does not exist")
if row["status"] != "dispatched":
if (
row["status"] == status
and row["response_digest"] == response_digest
and row["error_code"] == error_code
and row["error_message"] == safe_error
and row["prompt_tokens"] == prompt_tokens
and row["completion_tokens"] == completion_tokens
and row["cache_read_tokens"] == cache_read_tokens
and row["cache_write_tokens"] == cache_write_tokens
and row["finish_reason"] == finish_reason
):
return self._model_invocation_from_row(row)
raise InvalidRunTransitionError(
f"model invocation {invocation_id!r} is already "
f"{row['status']!r}"
)
connection.execute(
"""
UPDATE model_invocations
SET status = ?, response_json = ?, response_digest = ?,
prompt_tokens = ?, completion_tokens = ?,
cache_read_tokens = ?, cache_write_tokens = ?,
finish_reason = ?, error_code = ?, error_message = ?,
completed_at = ?
WHERE invocation_id = ? AND status = 'dispatched'
""",
(
status,
response_json,
response_digest,
prompt_tokens,
completion_tokens,
cache_read_tokens,
cache_write_tokens,
finish_reason,
error_code,
safe_error,
timestamp,
invocation_id,
),
)
event_payload: dict[str, Any] = {
"invocation_id": invocation_id,
"attempt_id": row["attempt_id"],
"agent_id": row["agent_id"],
"status": status,
"request_digest": row["request_digest"],
"response_digest": response_digest,
"finish_reason": finish_reason,
"usage": {
"prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens,
"cache_read_tokens": cache_read_tokens,
"cache_write_tokens": cache_write_tokens,
},
"error_code": error_code,
}
self._insert_model_invocation_event(
connection,
invocation_id=invocation_id,
event_type=status,
payload=event_payload,
created_at=timestamp,
)
self._append_event_in_transaction(
connection,
str(row["run_id"]),
RunEventDraft(
event_id=f"model-invocation:{invocation_id}:{status}",
event_type=f"model.invocation.{status}",
payload=event_payload,
created_at=timestamp,
),
)
updated = connection.execute(
"SELECT * FROM model_invocations WHERE invocation_id = ?",
(invocation_id,),
).fetchone()
assert updated is not None
return self._model_invocation_from_row(updated)
def get_model_invocation(
self, invocation_id: str
) -> ModelInvocationRecord | None:
with self._lock:
row = self._connection.execute(
"SELECT * FROM model_invocations WHERE invocation_id = ?",
(invocation_id,),
).fetchone()
return self._model_invocation_from_row(row) if row else None
def list_model_invocations(
self, run_id: str
) -> tuple[ModelInvocationRecord, ...]:
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM model_invocations
WHERE run_id = ? ORDER BY started_at, invocation_id
""",
(run_id,),
).fetchall()
return tuple(self._model_invocation_from_row(row) for row in rows)
def list_model_invocation_events(
self, invocation_id: str
) -> tuple[ModelInvocationEventRecord, ...]:
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM model_invocation_events
WHERE invocation_id = ? ORDER BY event_index
""",
(invocation_id,),
).fetchall()
return tuple(
self._model_invocation_event_from_row(row) for row in rows
)
def list_runs(
self,
*,
project_id: str,
statuses: tuple[str, ...] | None = None,
limit: int = 50,
) -> list[RunRecord]:
"""Read the canonical Runs for one Project, newest first."""
if not project_id.strip():
raise ValueError("project_id is required")
if limit < 1:
raise ValueError("run query limit must be positive")
parameters: list[Any] = [project_id]
query = "SELECT * FROM runs WHERE project_id = ?"
if statuses:
placeholders = ",".join("?" for _ in statuses)
query += f" AND status IN ({placeholders})"
parameters.extend(statuses)
query += " ORDER BY updated_at DESC, created_at DESC LIMIT ?"
parameters.append(limit)
with self._lock:
rows = self._connection.execute(query, parameters).fetchall()
return [self._run_from_row(row) for row in rows]
def list_recoverable_runs(self) -> list[RunRecord]:
"""Return Runs whose filesystem artifacts must precede reconciliation."""
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM runs
WHERE status IN ('pending', 'running', 'waiting_for_user')
OR (status = 'interrupted' AND cancel_request_id IS NOT NULL)
ORDER BY created_at, run_id
"""
).fetchall()
return [self._run_from_row(row) for row in rows]
def get_active_project_run(self, project_id: str) -> RunRecord | None:
"""Return the Run that owns the Project execution lease, if any."""
with self._lock:
row = self._connection.execute(
"""
SELECT runs.*
FROM project_run_execution_leases AS lease
JOIN runs ON runs.run_id = lease.run_id
WHERE lease.project_id = ?
""",
(project_id,),
).fetchone()
return self._run_from_row(row) if row is not None else None
def get_project_execution_state(
self, project_id: str
) -> ProjectExecutionStateRecord:
"""Return the latest semantic frontier for a Project.
``state_version`` increments only when a terminal Run changes the
canonical frontier digest. Transport retries and Runs that reproduce
the same frontier do not count as Project progress.
"""
if not project_id.strip():
raise ValueError("project_id is required")
with self._lock:
row = self._connection.execute(
"SELECT * FROM project_execution_states WHERE project_id = ?",
(project_id,),
).fetchone()
if row is not None:
return self._project_execution_state_from_row(row)
# Schema v20 already contains canonical terminal history. Lazily
# derive its first Project frontier after upgrading instead of making
# a real v1.0.2 Project look empty until another Run completes.
with self._write_transaction() as connection:
self._backfill_project_execution_state_in_transaction(
connection, project_id=project_id
)
row = connection.execute(
"SELECT * FROM project_execution_states WHERE project_id = ?",
(project_id,),
).fetchone()
if row is not None:
return self._project_execution_state_from_row(row)
return ProjectExecutionStateRecord(
project_id=project_id,
state_version=0,
frontier=None,
frontier_digest=None,
frontier_run_id=None,
updated_at=0.0,
)
def claim_continuation(
self,
*,
request_id: str,
project_id: str,
intent: str,
base_run_id: str | None,
next_action: str | None,
now: float | None = None,
) -> tuple[ContinuationClaimRecord, bool]:
"""Claim one semantic continuation at the current Project version.
Returns ``(claim, created)``. A different transport request attempting
the same fingerprint receives the existing claim with ``created=False``
and must not invoke the model again.
"""
if (
not request_id.strip()
or not project_id.strip()
or not intent.strip()
):
raise ValueError("continuation identity is required")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
self._backfill_project_execution_state_in_transaction(
connection, project_id=project_id
)
state = connection.execute(
"SELECT * FROM project_execution_states WHERE project_id = ?",
(project_id,),
).fetchone()
state_version = int(state["state_version"]) if state else 0
canonical = canonical_json(
{
"project_id": project_id,
"project_state_version": state_version,
"intent": intent,
"base_run_id": base_run_id,
"next_action": next_action,
}
)
fingerprint = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
by_request = connection.execute(
"SELECT * FROM continuation_claims WHERE request_id = ?",
(request_id,),
).fetchone()
if by_request is not None:
if by_request["fingerprint"] != fingerprint:
raise IdempotencyConflictError(
f"continuation request_id {request_id!r} was reused"
)
return self._continuation_claim_from_row(by_request), False
existing = connection.execute(
"SELECT * FROM continuation_claims WHERE fingerprint = ?",
(fingerprint,),
).fetchone()
if existing is not None:
return self._continuation_claim_from_row(existing), False
connection.execute(
"""
INSERT INTO continuation_claims(
fingerprint, request_id, project_id,
project_state_version, intent, base_run_id, next_action,
created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(
fingerprint,
request_id,
project_id,
state_version,
intent,
base_run_id,
next_action,
timestamp,
),
)
row = connection.execute(
"SELECT * FROM continuation_claims WHERE fingerprint = ?",
(fingerprint,),
).fetchone()
assert row is not None
return self._continuation_claim_from_row(row), True
def release_unadmitted_continuation(self, *, request_id: str) -> bool:
"""Release a reservation only when admission created no Attempt.
Environment or workspace preflight can fail after semantic admission
has claimed a continuation fingerprint. Keeping that reservation would
make a corrected retry with a new transport id look like duplicate
work even though the model never ran.
"""
if not request_id.strip():
raise ValueError("continuation request_id is required")
with self._write_transaction() as connection:
claim = connection.execute(
"SELECT request_id FROM continuation_claims WHERE request_id = ?",
(request_id,),
).fetchone()
if claim is None:
return False
admitted = connection.execute(
"SELECT 1 FROM run_attempts WHERE run_id = ? LIMIT 1",
(request_id,),
).fetchone()
if admitted is not None:
return False
deleted = connection.execute(
"DELETE FROM continuation_claims WHERE request_id = ?",
(request_id,),
)
return deleted.rowcount == 1
def put_context_projection_diagnostic(
self,
*,
projection_id: str,
project_id: str,
run_id: str,
source_event_ids: list[str] | tuple[str, ...],
source_memory_ids: list[str] | tuple[str, ...],
project_state_version: int,
projection_digest: str,
token_count: int,
now: float | None = None,
) -> ContextProjectionDiagnosticRecord:
if len(projection_digest) != 64:
raise ValueError("projection_digest must be sha256 hex")
if project_state_version < 0 or token_count < 0:
raise ValueError("projection counters must be non-negative")
event_ids_json = canonical_json(sorted(set(source_event_ids)))
memory_ids_json = canonical_json(sorted(set(source_memory_ids)))
timestamp = now if now is not None else time.time()
expected = (
project_id,
run_id,
event_ids_json,
memory_ids_json,
project_state_version,
projection_digest,
token_count,
)
with self._write_transaction() as connection:
existing = connection.execute(
"""SELECT * FROM context_projection_diagnostics
WHERE projection_id = ? OR (
run_id = ? AND projection_digest = ?
)""",
(projection_id, run_id, projection_digest),
).fetchone()
if existing is not None:
actual = (
existing["project_id"],
existing["run_id"],
existing["source_event_ids_json"],
existing["source_memory_ids_json"],
int(existing["project_state_version"]),
existing["projection_digest"],
int(existing["token_count"]),
)
if actual != expected:
raise IdempotencyConflictError(
f"projection_id {projection_id!r} was reused"
)
return self._context_projection_diagnostic_from_row(existing)
connection.execute(
"""
INSERT INTO context_projection_diagnostics(
projection_id, project_id, run_id,
source_event_ids_json, source_memory_ids_json,
project_state_version, projection_digest, token_count,
created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
projection_id,
project_id,
run_id,
event_ids_json,
memory_ids_json,
project_state_version,
projection_digest,
token_count,
timestamp,
),
)
row = connection.execute(
"""SELECT * FROM context_projection_diagnostics
WHERE projection_id = ?""",
(projection_id,),
).fetchone()
assert row is not None
return self._context_projection_diagnostic_from_row(row)
def list_context_projection_diagnostics(
self, *, run_id: str
) -> list[ContextProjectionDiagnosticRecord]:
with self._lock:
rows = self._connection.execute(
"""SELECT * FROM context_projection_diagnostics
WHERE run_id = ? ORDER BY created_at, projection_id""",
(run_id,),
).fetchall()
return [
self._context_projection_diagnostic_from_row(row)
for row in rows
]
def put_follow_up_request(
self,
*,
request_id: str,
project_id: str,
content: str,
attachment_paths: list[str] | tuple[str, ...] = (),
delivery_mode: str = "wait",
source: str = "local",
source_command_id: str | None = None,
now: float | None = None,
) -> FollowUpRequestRecord:
"""Durably enqueue one future Run instruction.
``request_id`` is also used as the future Run id by the renderer. A
lost HTTP response can therefore retry both queue creation and Run
admission without creating a duplicate Run.
"""
normalized_id = request_id.strip()
normalized_project = project_id.strip()
normalized_content = content.strip()
normalized_paths = tuple(str(path) for path in attachment_paths)
if (
not normalized_id
or not normalized_project
or not normalized_content
):
raise ValueError("request_id, project_id and content are required")
if delivery_mode not in {"wait", "send_now"}:
raise ValueError("unsupported follow-up delivery mode")
if source not in {"local", "remote_control", "scheduled"}:
raise ValueError("unsupported follow-up source")
normalized_command_id = (
source_command_id.strip()
if isinstance(source_command_id, str) and source_command_id.strip()
else None
)
if source == "remote_control" and normalized_command_id is None:
raise ValueError(
"remote_control follow-ups require source_command_id"
)
if source != "remote_control" and normalized_command_id is not None:
raise ValueError(
"source_command_id is reserved for remote_control"
)
if len(normalized_paths) > 32 or any(
not path.strip() for path in normalized_paths
):
raise ValueError("follow-up attachment paths are invalid")
paths_json = json.dumps(
normalized_paths,
ensure_ascii=False,
separators=(",", ":"),
)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
if normalized_command_id is not None:
command_row = connection.execute(
"""SELECT * FROM follow_up_requests
WHERE source_command_id = ?""",
(normalized_command_id,),
).fetchone()
if command_row is not None:
if command_row["request_id"] != normalized_id:
raise IdempotencyConflictError(
f"remote command {normalized_command_id!r} was "
"already mapped to another follow-up"
)
if (
command_row["project_id"] != normalized_project
or command_row["content"] != normalized_content
or command_row["attachment_paths_json"] != paths_json
):
raise IdempotencyConflictError(
f"remote command {normalized_command_id!r} was reused"
)
return self._follow_up_request_from_row(command_row)
existing = connection.execute(
"SELECT * FROM follow_up_requests WHERE request_id = ?",
(normalized_id,),
).fetchone()
if existing is not None:
if (
existing["project_id"] != normalized_project
or existing["content"] != normalized_content
or existing["attachment_paths_json"] != paths_json
or existing["source"] != source
or existing["source_command_id"] != normalized_command_id
):
raise IdempotencyConflictError(
f"follow-up request_id {normalized_id!r} was reused"
)
return self._follow_up_request_from_row(existing)
if delivery_mode == "send_now":
connection.execute(
"""UPDATE follow_up_requests
SET delivery_mode = 'wait', updated_at = ?
WHERE project_id = ? AND status = 'pending'
AND delivery_mode = 'send_now'""",
(timestamp, normalized_project),
)
connection.execute(
"""
INSERT INTO follow_up_requests(
request_id, project_id, content, attachment_paths_json,
delivery_mode, status, admitted_run_id, last_error,
created_at, updated_at, source, source_command_id
) VALUES (?, ?, ?, ?, ?, 'pending', NULL, NULL, ?, ?, ?, ?)
""",
(
normalized_id,
normalized_project,
normalized_content,
paths_json,
delivery_mode,
timestamp,
timestamp,
source,
normalized_command_id,
),
)
row = connection.execute(
"SELECT * FROM follow_up_requests WHERE request_id = ?",
(normalized_id,),
).fetchone()
assert row is not None
return self._follow_up_request_from_row(row)
def list_follow_up_requests(
self,
*,
project_id: str,
statuses: tuple[str, ...] = ("pending",),
) -> list[FollowUpRequestRecord]:
if not project_id.strip():
raise ValueError("project_id is required")
if not statuses:
return []
if any(
status not in {"pending", "admitted", "cancelled"}
for status in statuses
):
raise ValueError("unsupported follow-up status")
selected_statuses = set(statuses)
with self._lock:
rows = self._connection.execute(
"""SELECT * FROM follow_up_requests
WHERE project_id = ?
AND (
(status = 'pending' AND ?)
OR (status = 'admitted' AND ?)
OR (status = 'cancelled' AND ?)
)
ORDER BY CASE delivery_mode WHEN 'send_now' THEN 0 ELSE 1 END,
created_at, request_id""",
(
project_id,
"pending" in selected_statuses,
"admitted" in selected_statuses,
"cancelled" in selected_statuses,
),
).fetchall()
return [self._follow_up_request_from_row(row) for row in rows]
def list_pending_follow_up_requests_by_source(
self, *, source: str
) -> list[FollowUpRequestRecord]:
if source not in {"local", "remote_control", "scheduled"}:
raise ValueError("unsupported follow-up source")
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM follow_up_requests
WHERE source = ? AND status = 'pending'
ORDER BY project_id,
CASE delivery_mode WHEN 'send_now' THEN 0 ELSE 1 END,
created_at, request_id
""",
(source,),
).fetchall()
return [self._follow_up_request_from_row(row) for row in rows]
def get_follow_up_request_by_source_command_id(
self, *, source_command_id: str
) -> FollowUpRequestRecord | None:
"""Return the durable queue outcome owned by one remote command.
This lookup is intentionally not limited to pending rows. The Remote
Control bridge uses it after a renderer restart to reconstruct the
enqueue acknowledgement even when the follow-up was already admitted
or rejected by Run admission.
"""
normalized_command_id = source_command_id.strip()
if not normalized_command_id:
raise ValueError("source_command_id is required")
with self._lock:
row = self._connection.execute(
"""SELECT * FROM follow_up_requests
WHERE source = 'remote_control' AND source_command_id = ?""",
(normalized_command_id,),
).fetchone()
return (
self._follow_up_request_from_row(row)
if row is not None
else None
)
def set_follow_up_delivery_mode(
self,
*,
request_id: str,
project_id: str,
delivery_mode: str,
now: float | None = None,
) -> FollowUpRequestRecord:
if delivery_mode not in {"wait", "send_now"}:
raise ValueError("unsupported follow-up delivery mode")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM follow_up_requests WHERE request_id = ?",
(request_id,),
).fetchone()
if row is None or row["project_id"] != project_id:
raise RunNotFoundError(
f"follow-up request_id {request_id!r} does not exist"
)
if row["status"] != "pending":
return self._follow_up_request_from_row(row)
if delivery_mode == "send_now":
# Only one message can own the interrupt-next position. This
# keeps the durable ordering identical to the renderer after
# restart instead of letting an older send_now row win.
connection.execute(
"""UPDATE follow_up_requests
SET delivery_mode = 'wait', updated_at = ?
WHERE project_id = ? AND status = 'pending'
AND delivery_mode = 'send_now' AND request_id != ?""",
(timestamp, project_id, request_id),
)
connection.execute(
"""UPDATE follow_up_requests
SET delivery_mode = ?, updated_at = ? WHERE request_id = ?""",
(delivery_mode, timestamp, request_id),
)
updated = connection.execute(
"SELECT * FROM follow_up_requests WHERE request_id = ?",
(request_id,),
).fetchone()
assert updated is not None
return self._follow_up_request_from_row(updated)
def cancel_follow_up_request(
self,
*,
request_id: str,
project_id: str,
now: float | None = None,
) -> FollowUpRequestRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM follow_up_requests WHERE request_id = ?",
(request_id,),
).fetchone()
if row is None or row["project_id"] != project_id:
raise RunNotFoundError(
f"follow-up request_id {request_id!r} does not exist"
)
if row["status"] == "pending":
connection.execute(
"""UPDATE follow_up_requests
SET status = 'cancelled', updated_at = ? WHERE request_id = ?""",
(timestamp, request_id),
)
updated = connection.execute(
"SELECT * FROM follow_up_requests WHERE request_id = ?",
(request_id,),
).fetchone()
assert updated is not None
return self._follow_up_request_from_row(updated)
def reject_follow_up_request(
self,
*,
request_id: str,
project_id: str,
error: str,
now: float | None = None,
) -> FollowUpRequestRecord:
"""Close a pending request that cannot be semantically admitted.
Network and active-Run conflicts remain pending and retryable. This
transition is reserved for durable admission outcomes that require a
different user instruction, such as an ambiguous or duplicate weak
continuation. The cancelled row remains the audit record.
"""
normalized_error = error.strip()
if not normalized_error:
raise ValueError("follow-up rejection error is required")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM follow_up_requests WHERE request_id = ?",
(request_id,),
).fetchone()
if row is None or row["project_id"] != project_id:
raise RunNotFoundError(
f"follow-up request_id {request_id!r} does not exist"
)
if row["status"] == "pending":
connection.execute(
"""UPDATE follow_up_requests
SET status = 'cancelled', last_error = ?, updated_at = ?
WHERE request_id = ? AND status = 'pending'""",
(normalized_error[:4000], timestamp, request_id),
)
updated = connection.execute(
"SELECT * FROM follow_up_requests WHERE request_id = ?",
(request_id,),
).fetchone()
assert updated is not None
return self._follow_up_request_from_row(updated)
def mark_follow_up_admitted(
self,
*,
request_id: str,
project_id: str,
run_id: str,
now: float | None = None,
) -> FollowUpRequestRecord:
timestamp = now if now is not None else time.time()
if run_id != request_id:
raise IdempotencyConflictError(
"a follow-up request must be admitted as its deterministic Run"
)
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM follow_up_requests WHERE request_id = ?",
(request_id,),
).fetchone()
run = connection.execute(
"SELECT project_id, status FROM runs WHERE run_id = ?",
(run_id,),
).fetchone()
if row is None or row["project_id"] != project_id:
raise RunNotFoundError(
f"follow-up request_id {request_id!r} does not exist"
)
if run is None or run["project_id"] != project_id:
raise RunNotFoundError(
f"follow-up Run {run_id!r} does not exist in this Project"
)
if run["status"] in {"completed", "failed", "cancelled"}:
raise InvalidRunTransitionError(
"a follow-up cannot be admitted to a terminal Run"
)
attempt = connection.execute(
"SELECT 1 FROM run_attempts WHERE run_id = ? LIMIT 1",
(run_id,),
).fetchone()
if attempt is None:
raise InvalidRunTransitionError(
"a follow-up cannot be admitted before Run admission"
)
if row["status"] == "admitted":
if row["admitted_run_id"] != run_id:
raise IdempotencyConflictError(
"follow-up was admitted as a different Run"
)
return self._follow_up_request_from_row(row)
if row["status"] != "pending":
raise InvalidRunTransitionError(
"a cancelled follow-up cannot be admitted"
)
connection.execute(
"""UPDATE follow_up_requests
SET status = 'admitted', admitted_run_id = ?, last_error = NULL,
updated_at = ? WHERE request_id = ?""",
(run_id, timestamp, request_id),
)
updated = connection.execute(
"SELECT * FROM follow_up_requests WHERE request_id = ?",
(request_id,),
).fetchone()
assert updated is not None
return self._follow_up_request_from_row(updated)
def ensure_project_workspace_binding(
self,
*,
project_id: str,
repository_id: str,
checkout_id: str,
checkout_mode: str,
target_ref: str,
worktree_path: str,
now: float | None = None,
) -> ProjectWorkspaceBindingRecord:
"""Persist a Project's physical checkout without silently rebinding it.
Ordinary Projects share the Space primary checkout. Explicit user
branch/worktree creation uses a different checkout id and must go
through ``update_project_workspace_binding`` after the worktree exists.
"""
values = {
"project_id": project_id.strip(),
"repository_id": repository_id.strip(),
"checkout_id": checkout_id.strip(),
"target_ref": target_ref.strip(),
"worktree_path": worktree_path.strip(),
}
if any(not value for value in values.values()):
raise ValueError("Project workspace binding identity is required")
if any(len(value) > 4096 for value in values.values()):
raise ValueError("Project workspace binding identity is too long")
if checkout_mode not in {"primary_checkout", "explicit_worktree"}:
raise ValueError("invalid Project checkout mode")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
existing = connection.execute(
"SELECT * FROM project_workspace_bindings WHERE project_id = ?",
(values["project_id"],),
).fetchone()
if existing is not None:
persisted = (
existing["repository_id"],
existing["checkout_id"],
existing["checkout_mode"],
existing["target_ref"],
existing["worktree_path"],
)
requested = (
values["repository_id"],
values["checkout_id"],
checkout_mode,
values["target_ref"],
values["worktree_path"],
)
if persisted != requested:
raise IdempotencyConflictError(
f"Project {project_id!r} already has another "
"workspace binding"
)
return self._project_workspace_binding_from_row(existing)
repository = connection.execute(
"""
SELECT repository_role FROM git_repositories
WHERE repository_id = ?
""",
(values["repository_id"],),
).fetchone()
if (
repository is None
or repository["repository_role"] != "content"
):
raise RunNotFoundError(
f"Content Repository {repository_id!r} does not exist"
)
connection.execute(
"""
INSERT INTO project_workspace_bindings(
project_id, repository_id, checkout_id, checkout_mode,
target_ref, worktree_path, version, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)
""",
(
values["project_id"],
values["repository_id"],
values["checkout_id"],
checkout_mode,
values["target_ref"],
values["worktree_path"],
timestamp,
timestamp,
),
)
row = connection.execute(
"SELECT * FROM project_workspace_bindings WHERE project_id = ?",
(values["project_id"],),
).fetchone()
assert row is not None
return self._project_workspace_binding_from_row(row)
def get_project_workspace_binding(
self,
project_id: str,
) -> ProjectWorkspaceBindingRecord | None:
with self._lock:
row = self._connection.execute(
"SELECT * FROM project_workspace_bindings WHERE project_id = ?",
(project_id,),
).fetchone()
return (
self._project_workspace_binding_from_row(row)
if row is not None
else None
)
def update_project_workspace_binding(
self,
*,
project_id: str,
expected_version: int,
checkout_id: str,
checkout_mode: str,
target_ref: str,
worktree_path: str,
now: float | None = None,
) -> ProjectWorkspaceBindingRecord:
"""CAS-switch a Project after an explicit checkout operation."""
if expected_version < 1:
raise ValueError(
"expected Project workspace version must be positive"
)
values = {
"checkout_id": checkout_id.strip(),
"target_ref": target_ref.strip(),
"worktree_path": worktree_path.strip(),
}
if any(not value for value in values.values()):
raise ValueError("Project workspace binding identity is required")
if checkout_mode not in {"primary_checkout", "explicit_worktree"}:
raise ValueError("invalid Project checkout mode")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
cursor = connection.execute(
"""
UPDATE project_workspace_bindings
SET checkout_id = ?, checkout_mode = ?, target_ref = ?,
worktree_path = ?, version = version + 1, updated_at = ?
WHERE project_id = ? AND version = ?
""",
(
values["checkout_id"],
checkout_mode,
values["target_ref"],
values["worktree_path"],
timestamp,
project_id,
expected_version,
),
)
if cursor.rowcount != 1:
current = connection.execute(
"""
SELECT version FROM project_workspace_bindings
WHERE project_id = ?
""",
(project_id,),
).fetchone()
if current is None:
raise RunNotFoundError(
f"Project {project_id!r} has no workspace binding"
)
raise OptimisticConcurrencyError(
f"Project {project_id!r} workspace binding changed"
)
row = connection.execute(
"SELECT * FROM project_workspace_bindings WHERE project_id = ?",
(project_id,),
).fetchone()
assert row is not None
return self._project_workspace_binding_from_row(row)
def enqueue_workspace_writer(
self,
*,
request_id: str,
repository_id: str,
checkout_id: str,
task_id: str,
project_id: str,
target_ref: str,
reason: str,
now: float | None = None,
) -> WorkspaceWriterRequestRecord:
"""Acquire or durably queue one Task for a physical checkout.
The first request acquires the lease in the same transaction. Later
requests remain FIFO queued even when they belong to another Project.
Repeating one request id with the same payload is idempotent.
"""
values = {
"request_id": request_id.strip(),
"repository_id": repository_id.strip(),
"checkout_id": checkout_id.strip(),
"task_id": task_id.strip(),
"project_id": project_id.strip(),
"target_ref": target_ref.strip(),
"reason": reason.strip(),
}
if any(not value for value in values.values()):
raise ValueError("workspace writer identity is required")
if any(len(value) > 512 for value in values.values()):
raise ValueError("workspace writer identity is too long")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
existing = connection.execute(
"SELECT * FROM workspace_writer_requests WHERE request_id = ?",
(values["request_id"],),
).fetchone()
if existing is not None:
persisted = (
existing["repository_id"],
existing["checkout_id"],
existing["task_id"],
existing["project_id"],
existing["target_ref"],
existing["reason"],
)
requested = (
values["repository_id"],
values["checkout_id"],
values["task_id"],
values["project_id"],
values["target_ref"],
values["reason"],
)
if persisted != requested:
raise IdempotencyConflictError(
f"workspace writer request_id {request_id!r} was reused"
)
return self._workspace_writer_request_from_row(
connection,
existing,
)
repository = connection.execute(
"SELECT 1 FROM git_repositories WHERE repository_id = ?",
(values["repository_id"],),
).fetchone()
if repository is None:
raise RunNotFoundError(
f"Git repository {repository_id!r} does not exist"
)
task_request = connection.execute(
"""
SELECT request_id FROM workspace_writer_requests
WHERE repository_id = ? AND checkout_id = ? AND task_id = ?
""",
(
values["repository_id"],
values["checkout_id"],
values["task_id"],
),
).fetchone()
if task_request is not None:
raise IdempotencyConflictError(
f"Task {task_id!r} already has workspace writer request "
f"{task_request['request_id']!r}"
)
connection.execute(
"""
INSERT INTO workspace_writer_requests(
request_id, repository_id, checkout_id, task_id,
project_id, target_ref, reason, status, created_at,
acquired_at, finished_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, 'queued', ?, NULL, NULL, ?)
""",
(
values["request_id"],
values["repository_id"],
values["checkout_id"],
values["task_id"],
values["project_id"],
values["target_ref"],
values["reason"],
timestamp,
timestamp,
),
)
lease = connection.execute(
"""
SELECT 1 FROM workspace_writer_leases
WHERE repository_id = ? AND checkout_id = ?
""",
(values["repository_id"], values["checkout_id"]),
).fetchone()
if lease is None:
self._acquire_workspace_writer_in_transaction(
connection,
request_id=values["request_id"],
now=timestamp,
)
row = connection.execute(
"SELECT * FROM workspace_writer_requests WHERE request_id = ?",
(values["request_id"],),
).fetchone()
assert row is not None
return self._workspace_writer_request_from_row(connection, row)
def get_workspace_writer_request(
self,
request_id: str,
) -> WorkspaceWriterRequestRecord | None:
with self._lock:
row = self._connection.execute(
"SELECT * FROM workspace_writer_requests WHERE request_id = ?",
(request_id,),
).fetchone()
return (
self._workspace_writer_request_from_row(self._connection, row)
if row is not None
else None
)
def get_workspace_writer_lease(
self,
*,
repository_id: str,
checkout_id: str,
) -> WorkspaceWriterLeaseRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM workspace_writer_leases
WHERE repository_id = ? AND checkout_id = ?
""",
(repository_id, checkout_id),
).fetchone()
return (
self._workspace_writer_lease_from_row(row)
if row is not None
else None
)
def list_workspace_writer_requests(
self,
*,
repository_id: str,
checkout_id: str,
include_terminal: bool = False,
) -> list[WorkspaceWriterRequestRecord]:
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM workspace_writer_requests
WHERE repository_id = ? AND checkout_id = ?
AND (? OR status IN ('queued', 'acquired'))
ORDER BY
CASE status
WHEN 'acquired' THEN 0
WHEN 'queued' THEN 1
ELSE 2
END,
created_at, request_id
""",
(repository_id, checkout_id, include_terminal),
).fetchall()
return [
self._workspace_writer_request_from_row(self._connection, row)
for row in rows
]
def release_workspace_writer(
self,
*,
request_id: str,
task_id: str,
now: float | None = None,
) -> WorkspaceWriterReleaseResult:
return self._finish_workspace_writer(
request_id=request_id,
task_id=task_id,
final_status="released",
now=now,
)
def interrupt_workspace_writer(
self,
*,
request_id: str,
task_id: str,
now: float | None = None,
) -> WorkspaceWriterReleaseResult:
return self._finish_workspace_writer(
request_id=request_id,
task_id=task_id,
final_status="interrupted",
now=now,
)
def _finish_workspace_writer(
self,
*,
request_id: str,
task_id: str,
final_status: str,
now: float | None,
) -> WorkspaceWriterReleaseResult:
if final_status not in {"released", "interrupted"}:
raise ValueError("invalid workspace writer terminal status")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM workspace_writer_requests WHERE request_id = ?",
(request_id,),
).fetchone()
if row is None:
raise RunNotFoundError(
f"workspace writer request_id {request_id!r} does not exist"
)
if row["task_id"] != task_id:
raise InvalidRunTransitionError(
"workspace writer can only be finished by its owning Task"
)
if row["status"] in {"released", "interrupted"}:
return WorkspaceWriterReleaseResult(
finished=self._workspace_writer_request_from_row(
connection,
row,
),
next_acquired=None,
)
next_acquired = None
if row["status"] == "acquired":
lease = connection.execute(
"""
SELECT * FROM workspace_writer_leases
WHERE repository_id = ? AND checkout_id = ?
""",
(row["repository_id"], row["checkout_id"]),
).fetchone()
if lease is None or lease["request_id"] != request_id:
raise InvalidRunTransitionError(
"workspace writer lease ownership is inconsistent"
)
connection.execute(
"""
DELETE FROM workspace_writer_leases
WHERE repository_id = ? AND checkout_id = ?
""",
(row["repository_id"], row["checkout_id"]),
)
connection.execute(
"""
UPDATE workspace_writer_requests
SET status = ?, finished_at = ?, updated_at = ?
WHERE request_id = ?
""",
(final_status, timestamp, timestamp, request_id),
)
if row["status"] == "acquired":
queued = connection.execute(
"""
SELECT request_id FROM workspace_writer_requests
WHERE repository_id = ? AND checkout_id = ?
AND status = 'queued'
ORDER BY created_at, request_id
LIMIT 1
""",
(row["repository_id"], row["checkout_id"]),
).fetchone()
if queued is not None:
self._acquire_workspace_writer_in_transaction(
connection,
request_id=queued["request_id"],
now=timestamp,
)
acquired_row = connection.execute(
"""
SELECT * FROM workspace_writer_requests
WHERE request_id = ?
""",
(queued["request_id"],),
).fetchone()
assert acquired_row is not None
next_acquired = self._workspace_writer_request_from_row(
connection,
acquired_row,
)
finished_row = connection.execute(
"SELECT * FROM workspace_writer_requests WHERE request_id = ?",
(request_id,),
).fetchone()
assert finished_row is not None
return WorkspaceWriterReleaseResult(
finished=self._workspace_writer_request_from_row(
connection,
finished_row,
),
next_acquired=next_acquired,
)
@staticmethod
def _acquire_workspace_writer_in_transaction(
connection: sqlite3.Connection,
*,
request_id: str,
now: float,
) -> None:
row = connection.execute(
"SELECT * FROM workspace_writer_requests WHERE request_id = ?",
(request_id,),
).fetchone()
if row is None or row["status"] != "queued":
raise InvalidRunTransitionError(
"only a queued workspace writer can acquire the lease"
)
connection.execute(
"""
INSERT INTO workspace_writer_leases(
repository_id, checkout_id, request_id, task_id, project_id,
target_ref, acquired_at, version
) VALUES (?, ?, ?, ?, ?, ?, ?, 1)
""",
(
row["repository_id"],
row["checkout_id"],
row["request_id"],
row["task_id"],
row["project_id"],
row["target_ref"],
now,
),
)
connection.execute(
"""
UPDATE workspace_writer_requests
SET status = 'acquired', acquired_at = ?, updated_at = ?
WHERE request_id = ? AND status = 'queued'
""",
(now, now, request_id),
)
def list_all_runs(self) -> list[RunRecord]:
"""Return canonical Runs for startup projection reconciliation."""
with self._lock:
rows = self._connection.execute(
"SELECT * FROM runs ORDER BY created_at"
).fetchall()
return [self._run_from_row(row) for row in rows]
def get_cloud_project_cursor(self, project_id: str) -> int:
"""Return the last canonical Cloud cursor imported into this device."""
with self._lock:
row = self._connection.execute(
"SELECT last_cursor FROM cloud_project_replicas WHERE project_id = ?",
(project_id,),
).fetchone()
return int(row["last_cursor"]) if row is not None else 0
def import_cloud_project_page(
self,
*,
project_id: str,
after_cursor: int,
next_cursor: int,
events: list[CloudRunEventReplica],
now: float | None = None,
) -> int:
"""Import a canonical PG event page without creating a new upload outbox.
This is a read replica repair path. Imported history is deliberately
marked ``cloud_restore`` so it can be rendered after local data loss,
but cannot be mistaken for a locally executable Run with a bound
workspace and credentials.
"""
if not project_id.strip():
raise ValueError("project_id is required")
if after_cursor < 0 or next_cursor < after_cursor:
raise ValueError("invalid cloud cursor range")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
cursor_row = connection.execute(
"SELECT last_cursor FROM cloud_project_replicas WHERE project_id = ?",
(project_id,),
).fetchone()
persisted_cursor = (
int(cursor_row["last_cursor"]) if cursor_row is not None else 0
)
if persisted_cursor != after_cursor:
raise OptimisticConcurrencyError(
f"project {project_id!r} cloud cursor expected {after_cursor}, "
f"found {persisted_cursor}"
)
expected_cursors = list(
range(after_cursor + 1, after_cursor + 1 + len(events))
)
if [event.cloud_cursor for event in events] != expected_cursors:
raise IdempotencyConflictError(
f"project {project_id!r} cloud event page is not contiguous"
)
if (
events[-1].cloud_cursor if events else after_cursor
) != next_cursor:
raise IdempotencyConflictError(
f"project {project_id!r} next_cursor does not match its event page"
)
for event in events:
if event.project_id != project_id:
raise IdempotencyConflictError(
f"event {event.event_id!r} belongs to another project"
)
if event.run_sequence < 1 or event.run_version < 1:
raise ValueError(
"cloud Run sequence and version must be positive"
)
payload_json = json.dumps(
event.payload,
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
)
run = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (event.run_id,)
).fetchone()
if run is None:
connection.execute(
"""
INSERT INTO runs(
run_id, project_id, status, version,
active_attempt_id, deadline_at,
timeout_policy_version, created_at, updated_at,
origin, resume_blocked_reason
) VALUES (?, ?, 'interrupted', 0, NULL, NULL, 'v1',
?, ?, 'cloud_restore',
'cloud_restore_workspace_missing')
""",
(
event.run_id,
project_id,
event.created_at,
event.created_at,
),
)
run = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (event.run_id,)
).fetchone()
assert run is not None
if run["project_id"] != project_id:
raise IdempotencyConflictError(
f"run_id {event.run_id!r} belongs to another project"
)
duplicate = connection.execute(
"SELECT * FROM run_events WHERE event_id = ?",
(event.event_id,),
).fetchone()
sequence_owner = connection.execute(
"SELECT * FROM run_events WHERE run_id = ? AND sequence = ?",
(event.run_id, event.run_sequence),
).fetchone()
existing = duplicate or sequence_owner
if existing is not None:
existing_payload = json.loads(existing["payload_json"])
payload_matches = (
existing_payload == event.payload
or cloud_event_payload(
str(existing["event_type"]), existing_payload
)
== event.payload
)
if (
existing["event_id"] != event.event_id
or existing["run_id"] != event.run_id
or int(existing["sequence"]) != event.run_sequence
or int(existing["run_version"]) != event.run_version
or existing["event_type"] != event.event_type
or not payload_matches
or existing["legacy_step"] != event.legacy_step
):
raise IdempotencyConflictError(
f"cloud event {event.event_id!r} conflicts with local history"
)
else:
connection.execute(
"""
INSERT INTO run_events(
event_id, run_id, sequence, run_version, event_type,
payload_json, legacy_step, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(
event.event_id,
event.run_id,
event.run_sequence,
event.run_version,
event.event_type,
payload_json,
event.legacy_step,
event.created_at,
),
)
projected_status = self._terminal_status_for_event(
RunEventDraft(
event_id=event.event_id,
event_type=event.event_type,
payload=event.payload,
legacy_step=event.legacy_step,
created_at=event.created_at,
)
)
if run["origin"] == "cloud_restore":
connection.execute(
"""
UPDATE runs
SET version = MAX(version, ?),
status = COALESCE(?, status),
updated_at = MAX(updated_at, ?)
WHERE run_id = ?
""",
(
event.run_version,
projected_status,
event.created_at,
event.run_id,
),
)
connection.execute(
"""
INSERT INTO cloud_project_replicas(project_id, last_cursor, last_synced_at)
VALUES (?, ?, ?)
ON CONFLICT(project_id) DO UPDATE SET
last_cursor = excluded.last_cursor,
last_synced_at = excluded.last_synced_at
""",
(project_id, next_cursor, timestamp),
)
return next_cursor
def reconcile_cloud_project_runs(
self,
*,
project_id: str,
current_cursor: int,
runs: list[CloudRunReplica],
now: float | None = None,
) -> None:
"""Apply Cloud aggregate status after every event page was imported."""
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
cursor = connection.execute(
"SELECT last_cursor FROM cloud_project_replicas WHERE project_id = ?",
(project_id,),
).fetchone()
local_cursor = int(cursor["last_cursor"]) if cursor else 0
if local_cursor != current_cursor:
raise OptimisticConcurrencyError(
f"project {project_id!r} bootstrap ended at {local_cursor}, "
f"server watermark is {current_cursor}"
)
for replica in runs:
row = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (replica.run_id,)
).fetchone()
if row is None:
# A canonical Run without events is not executable either;
# retain it so the UI can show that recovery is incomplete.
connection.execute(
"""
INSERT INTO runs(
run_id, project_id, status, version,
active_attempt_id, deadline_at,
timeout_policy_version, created_at, updated_at,
origin, resume_blocked_reason
) VALUES (?, ?, ?, ?, NULL, NULL, 'v1', ?, ?,
'cloud_restore',
'cloud_restore_workspace_missing')
""",
(
replica.run_id,
project_id,
self._cloud_restored_status(replica.status),
max(0, replica.expected_next_run_sequence - 1),
replica.updated_at,
replica.updated_at,
),
)
continue
if row["project_id"] != project_id:
raise IdempotencyConflictError(
f"run_id {replica.run_id!r} belongs to another project"
)
if row["origin"] == "cloud_restore":
connection.execute(
"""
UPDATE runs
SET status = ?, version = MAX(version, ?),
updated_at = MAX(updated_at, ?),
resume_blocked_reason = 'cloud_restore_workspace_missing'
WHERE run_id = ?
""",
(
self._cloud_restored_status(replica.status),
max(0, replica.expected_next_run_sequence - 1),
replica.updated_at,
replica.run_id,
),
)
connection.execute(
"""
UPDATE cloud_project_replicas
SET last_synced_at = ? WHERE project_id = ?
""",
(timestamp, project_id),
)
def append_event(
self,
run_id: str,
draft: RunEventDraft,
*,
expected_version: int | None = None,
expected_project_id: str | None = None,
) -> CommittedRunEvent:
payload_json = json.dumps(
dict(draft.payload),
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
)
with self._write_transaction() as connection:
terminal_status = self._terminal_status_for_event(draft)
event = self._append_event_in_transaction(
connection,
run_id,
draft,
payload_json=payload_json,
expected_version=expected_version,
expected_project_id=expected_project_id,
run_status=terminal_status,
clear_active_attempt=terminal_status is not None,
)
if terminal_status is not None:
attempt_status = (
"completed"
if terminal_status == "completed"
else terminal_status
)
connection.execute(
"""
UPDATE run_attempts
SET status = ?, ended_at = COALESCE(ended_at, ?),
outcome = COALESCE(outcome, ?)
WHERE run_id = ? AND status IN ('pending', 'running', 'waiting_for_user')
""",
(
attempt_status,
draft.created_at,
draft.event_type,
run_id,
),
)
return event
def append_artifact_manifest_events(
self,
run_id: str,
drafts: list[RunEventDraft] | tuple[RunEventDraft, ...],
*,
expected_project_id: str | None = None,
) -> CommittedRunEvent:
"""Commit one Run's Artifact lifecycle and manifest barrier once.
Artifact discovery runs before this transaction and two terminal paths
may race to publish its result. While a Run remains non-terminal a
later scan may append a corrected manifest (for example after crash
recovery). Once terminal, ``BEGIN IMMEDIATE`` plus the in-lock barrier
lookup freezes and returns the latest committed manifest.
"""
if (
not drafts
or drafts[-1].event_type != "artifact.manifest.finalized"
):
raise ValueError(
"artifact batch must end with a finalized manifest"
)
if any(
self._terminal_status_for_event(draft) is not None
for draft in drafts
):
raise ValueError("artifact batches cannot contain terminal events")
with self._write_transaction() as connection:
existing = connection.execute(
"""
SELECT event_id, run_id, sequence, run_version, event_type,
payload_json, legacy_step, created_at
FROM run_events
WHERE run_id = ? AND event_type = 'artifact.manifest.finalized'
ORDER BY sequence DESC
LIMIT 1
""",
(run_id,),
).fetchone()
run = connection.execute(
"SELECT status, project_id FROM runs WHERE run_id = ?",
(run_id,),
).fetchone()
if run is None:
raise RunNotFoundError(f"run_id {run_id!r} does not exist")
if existing is not None and run["status"] in {
"completed",
"failed",
"cancelled",
}:
return self._event_from_row(existing)
if existing is not None:
existing_payload = json.loads(existing["payload_json"])
incoming_payload = dict(drafts[-1].payload)
if (
existing_payload.get("scan_status") == "complete"
and incoming_payload.get("scan_status") != "complete"
):
return self._event_from_row(existing)
committed: list[CommittedRunEvent] = []
for draft in drafts:
committed.append(
self._append_event_in_transaction(
connection,
run_id,
draft,
expected_project_id=expected_project_id,
)
)
self._enqueue_artifact_uploads_in_transaction(
connection,
run_id=run_id,
project_id=str(run["project_id"]),
manifest_payload=dict(drafts[-1].payload),
created_at=float(drafts[-1].created_at),
)
return committed[-1]
@staticmethod
def _enqueue_artifact_uploads_in_transaction(
connection: sqlite3.Connection,
*,
run_id: str,
project_id: str,
manifest_payload: dict[str, Any],
created_at: float,
) -> None:
artifacts = manifest_payload.get("artifacts")
if not isinstance(artifacts, list):
return
current_ids: list[str] = []
for raw in artifacts:
if not isinstance(raw, dict):
continue
artifact_id = str(raw.get("artifact_id") or "").strip()
local_path = str(raw.get("path") or "").strip()
if (
not artifact_id
or not local_path
or raw.get("uploadPolicy") != "agent_generated"
):
continue
current_ids.append(artifact_id)
connection.execute(
"""
INSERT OR IGNORE INTO artifact_upload_outbox(
artifact_id, run_id, project_id, local_path, filename,
relative_path, file_size, status, attempt_count,
next_attempt_at, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', 0, ?, ?, ?)
""",
(
artifact_id,
run_id,
project_id,
local_path,
str(raw.get("filename") or Path(local_path).name),
str(raw.get("relativePath") or Path(local_path).name),
max(0, int(raw.get("size") or 0)),
created_at,
created_at,
created_at,
),
)
if manifest_payload.get("scan_status") != "complete":
return
# A corrected non-terminal manifest may drop a vanished file. Do not
# upload a stale path that is no longer part of the canonical barrier.
if current_ids:
connection.execute(
"""
DELETE FROM artifact_upload_outbox
WHERE run_id = ? AND status = 'pending'
AND artifact_id NOT IN (SELECT value FROM json_each(?))
""",
(run_id, json.dumps(current_ids, separators=(",", ":"))),
)
else:
connection.execute(
"""
DELETE FROM artifact_upload_outbox
WHERE run_id = ? AND status = 'pending'
""",
(run_id,),
)
def complete_successful_run(
self,
run_id: str,
*,
assistant_final: RunEventDraft,
terminal: RunEventDraft,
artifact_manifest: CommittedRunEvent,
expected_project_id: str,
) -> tuple[CommittedRunEvent, CommittedRunEvent]:
"""Atomically commit the canonical result and successful Run terminal.
Artifact discovery intentionally happens before this short transaction.
A crash after its manifest barrier is safe because a non-terminal Run may
publish a later manifest generation; the successful assistant result and
``run.completed`` themselves never become separated.
"""
if assistant_final.event_type != "assistant.final":
raise ValueError("successful completion requires assistant.final")
if terminal.event_type != "run.completed":
raise ValueError("successful completion requires run.completed")
if artifact_manifest.run_id != run_id:
raise IdempotencyConflictError(
"Artifact manifest belongs to another Run"
)
if artifact_manifest.event_type != "artifact.manifest.finalized":
raise IdempotencyConflictError(
"Successful completion requires an Artifact manifest"
)
with self._write_transaction() as connection:
run = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
if run is None:
raise RunNotFoundError(f"run_id {run_id!r} does not exist")
if run["project_id"] != expected_project_id:
raise IdempotencyConflictError(
f"run_id {run_id!r} belongs to another project"
)
unresolved_tools = connection.execute(
"""
SELECT tool_call_id, safety_class, idempotency_key
FROM tool_calls
WHERE run_id = ? AND status IN ('dispatched', 'outcome_unknown')
ORDER BY created_at, tool_call_id
""",
(run_id,),
).fetchall()
blocking_tool = next(
(
tool
for tool in unresolved_tools
if self._tool_call_requires_fail_closed(tool)
),
None,
)
if blocking_tool is not None:
raise InvalidRunTransitionError(
"a Run with an unresolved Tool outcome that is "
"non-replayable cannot complete successfully "
f"({blocking_tool['tool_call_id']})"
)
# Artifact discovery happens outside this short transaction and
# two terminal paths may race. Pin the newest committed manifest
# under the same writer lock as run.completed; never trust the
# manifest object observed before this transaction began.
latest_manifest = self._latest_artifact_manifest_in_transaction(
connection,
run_id,
)
terminal_payload = {
**dict(terminal.payload),
"artifact_manifest_event_id": latest_manifest.event_id,
"artifact_count": int(
latest_manifest.payload.get("artifact_count", 0)
),
"result_event_id": assistant_final.event_id,
}
terminal_draft = RunEventDraft(
event_id=terminal.event_id,
event_type=terminal.event_type,
payload=terminal_payload,
legacy_step=terminal.legacy_step,
created_at=terminal.created_at,
)
result_event = self._append_event_in_transaction(
connection,
run_id,
assistant_final,
expected_project_id=expected_project_id,
allow_assistant_final=True,
)
terminal_event = self._append_event_in_transaction(
connection,
run_id,
terminal_draft,
expected_project_id=expected_project_id,
run_status="completed",
clear_active_attempt=True,
)
connection.execute(
"""
UPDATE run_attempts
SET status = 'completed', ended_at = COALESCE(ended_at, ?),
outcome = COALESCE(outcome, 'run.completed')
WHERE run_id = ?
AND status IN ('pending', 'running', 'waiting_for_user')
""",
(terminal_draft.created_at, run_id),
)
return result_event, terminal_event
def append_terminal_with_latest_artifact_manifest(
self,
run_id: str,
draft: RunEventDraft,
*,
expected_project_id: str | None = None,
) -> CommittedRunEvent:
"""Atomically bind a terminal outcome to the latest Artifact manifest."""
terminal_status = self._terminal_status_for_event(draft)
if terminal_status not in {"completed", "failed", "cancelled"}:
raise ValueError(
"Artifact terminal commit requires a terminal Run event"
)
with self._write_transaction() as connection:
latest_manifest = self._latest_artifact_manifest_in_transaction(
connection,
run_id,
)
enriched = RunEventDraft(
event_id=draft.event_id,
event_type=draft.event_type,
payload={
**dict(draft.payload),
"artifact_manifest_event_id": latest_manifest.event_id,
"artifact_count": int(
latest_manifest.payload.get("artifact_count", 0)
),
},
legacy_step=draft.legacy_step,
created_at=draft.created_at,
)
event = self._append_event_in_transaction(
connection,
run_id,
enriched,
expected_project_id=expected_project_id,
run_status=terminal_status,
clear_active_attempt=True,
)
attempt_status = (
"completed"
if terminal_status == "completed"
else terminal_status
)
connection.execute(
"""
UPDATE run_attempts
SET status = ?, ended_at = COALESCE(ended_at, ?),
outcome = COALESCE(outcome, ?)
WHERE run_id = ?
AND status IN ('pending', 'running', 'waiting_for_user')
""",
(
attempt_status,
enriched.created_at,
enriched.event_type,
run_id,
),
)
return event
def _latest_artifact_manifest_in_transaction(
self,
connection: sqlite3.Connection,
run_id: str,
) -> CommittedRunEvent:
row = connection.execute(
"""
SELECT event_id, run_id, sequence, run_version, event_type,
payload_json, legacy_step, created_at
FROM run_events
WHERE run_id = ?
AND event_type = 'artifact.manifest.finalized'
ORDER BY sequence DESC
LIMIT 1
""",
(run_id,),
).fetchone()
if row is None:
raise InvalidRunTransitionError(
f"run {run_id!r} cannot terminate before its Artifact manifest"
)
return self._event_from_row(row)
def list_events(
self,
run_id: str,
*,
after_sequence: int = 0,
limit: int | None = None,
) -> list[CommittedRunEvent]:
if limit is not None and limit < 1:
raise ValueError("event query limit must be positive")
query = """
SELECT event_id, run_id, sequence, run_version, event_type,
payload_json, legacy_step, created_at
FROM run_events
WHERE run_id = ? AND sequence > ?
ORDER BY sequence
"""
parameters: list[Any] = [run_id, after_sequence]
if limit is not None:
query += " LIMIT ?"
parameters.append(limit)
with self._lock:
rows = self._connection.execute(query, parameters).fetchall()
return [self._event_from_row(row) for row in rows]
def get_events_by_id(
self, event_ids: tuple[str, ...] | list[str]
) -> list[CommittedRunEvent]:
"""Return canonical events in caller order for provenance checks."""
identifiers = tuple(dict.fromkeys(event_ids))
if not identifiers:
return []
with self._lock:
rows = self._connection.execute(
"""
SELECT event_id, run_id, sequence, run_version, event_type,
payload_json, legacy_step, created_at
FROM run_events
WHERE event_id IN (SELECT value FROM json_each(?))
""",
(json.dumps(identifiers, separators=(",", ":")),),
).fetchall()
by_id = {row["event_id"]: self._event_from_row(row) for row in rows}
return [
by_id[event_id] for event_id in identifiers if event_id in by_id
]
def get_project_history_cursor(self, project_id: str) -> int:
"""Return the last committed Project History cursor."""
if not project_id.strip():
raise ValueError("project_id is required")
with self._lock:
row = self._connection.execute(
"""
SELECT next_cursor - 1 AS current_cursor
FROM project_history_cursors
WHERE project_id = ?
""",
(project_id,),
).fetchone()
return int(row["current_cursor"]) if row is not None else 0
def list_project_history_events(
self,
project_id: str,
*,
after_cursor: int = 0,
limit: int = 100,
) -> list[ProjectHistoryEventRecord]:
"""Read a bounded cross-Run Project History page."""
if not project_id.strip():
raise ValueError("project_id is required")
if after_cursor < 0:
raise ValueError("after_cursor must be non-negative")
if limit < 1 or limit > 500:
raise ValueError("history query limit must be between 1 and 500")
with self._lock:
rows = self._connection.execute(
"""
SELECT history.project_id, history.journal_cursor,
history.source_kind,
events.event_id, events.run_id, events.sequence,
events.run_version, events.event_type,
events.payload_json, events.legacy_step,
events.created_at
FROM project_history_events AS history
JOIN run_events AS events ON events.event_id = history.event_id
WHERE history.project_id = ? AND history.journal_cursor > ?
ORDER BY history.journal_cursor
LIMIT ?
""",
(project_id, after_cursor, limit),
).fetchall()
return [
ProjectHistoryEventRecord(
project_id=row["project_id"],
journal_cursor=int(row["journal_cursor"]),
event=self._event_from_row(row),
source_kind=row["source_kind"],
)
for row in rows
]
def ensure_memory_scope_state(
self,
scope_type: str,
scope_id: str,
*,
owner_kind: str = "desktop",
token_limit: int | None = None,
now: float | None = None,
) -> MemoryScopeStateRecord:
self._validate_memory_scope(scope_type, scope_id)
if owner_kind not in {"desktop", "cloud"}:
raise ValueError("invalid Memory owner_kind")
resolved_limit = (
token_limit or _MEMORY_DEFAULT_TOKEN_LIMITS[scope_type]
)
if resolved_limit < 1:
raise ValueError("Memory token_limit must be positive")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = self._ensure_memory_scope_state_in_transaction(
connection,
scope_type=scope_type,
scope_id=scope_id,
owner_kind=owner_kind,
token_limit=resolved_limit,
now=timestamp,
)
return self._memory_scope_state_from_row(row)
def bind_memory_scope_owner(
self,
scope_type: str,
scope_id: str,
*,
account_owner_id: str,
now: float | None = None,
) -> None:
"""Bind a local scope to one authenticated account exactly once."""
self._validate_memory_scope(scope_type, scope_id)
owner = account_owner_id.strip()
if not owner:
raise ValueError("Memory account owner is required")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
existing = connection.execute(
"""
SELECT account_owner_id FROM memory_scope_owners
WHERE scope_type = ? AND scope_id = ?
""",
(scope_type, scope_id),
).fetchone()
if existing is not None and existing["account_owner_id"] != owner:
raise IdempotencyConflictError(
f"Memory scope {scope_type}:{scope_id} belongs to "
"another account"
)
connection.execute(
"""
INSERT OR IGNORE INTO memory_scope_owners(
scope_type, scope_id, account_owner_id, bound_at
) VALUES (?, ?, ?, ?)
""",
(scope_type, scope_id, owner, timestamp),
)
def register_memory_scope_owner_candidates(
self,
*,
project_id: str,
space_id: str,
claimed_account_owner_id: str | None,
now: float | None = None,
) -> None:
"""Record an untrusted ownership claim for later Cloud confirmation."""
owner = str(claimed_account_owner_id or "").strip()
if not owner:
return
timestamp = now if now is not None else time.time()
candidates = (
("project", project_id),
("space", space_id),
("user", owner),
)
with self._write_transaction() as connection:
for scope_type, scope_id in candidates:
self._validate_memory_scope(scope_type, scope_id)
connection.execute(
"""
INSERT OR IGNORE INTO memory_scope_owner_candidates(
scope_type, scope_id, claimed_account_owner_id,
created_at
) VALUES (?, ?, ?, ?)
""",
(scope_type, scope_id, owner, timestamp),
)
def list_memory_scope_owner_candidates(
self,
account_owner_id: str,
) -> list[tuple[str, str]]:
"""Return untrusted claims for explicit server authorization."""
owner = account_owner_id.strip()
if not owner:
return []
with self._lock:
rows = self._connection.execute(
"""
SELECT scope_type, scope_id
FROM memory_scope_owner_candidates
WHERE claimed_account_owner_id = ?
ORDER BY scope_type, scope_id
""",
(owner,),
).fetchall()
return [(str(row["scope_type"]), str(row["scope_id"])) for row in rows]
def confirm_memory_scope_owner_candidates(
self,
account_owner_id: str,
authorized_scopes: list[tuple[str, str]],
*,
now: float | None = None,
) -> int:
"""Promote only scope keys explicitly authorized by eigent_server."""
owner = account_owner_id.strip()
if not owner:
raise ValueError("Memory account owner is required")
normalized = set(authorized_scopes)
for scope_type, scope_id in normalized:
self._validate_memory_scope(scope_type, scope_id)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
rows = connection.execute(
"""
SELECT scope_type, scope_id
FROM memory_scope_owner_candidates
WHERE claimed_account_owner_id = ?
ORDER BY scope_type, scope_id
""",
(owner,),
).fetchall()
promoted = 0
for row in rows:
key = (str(row["scope_type"]), str(row["scope_id"]))
if key not in normalized:
continue
existing = connection.execute(
"""
SELECT account_owner_id FROM memory_scope_owners
WHERE scope_type = ? AND scope_id = ?
""",
(row["scope_type"], row["scope_id"]),
).fetchone()
if existing is not None:
if existing["account_owner_id"] != owner:
continue
else:
connection.execute(
"""
INSERT INTO memory_scope_owners(
scope_type, scope_id, account_owner_id, bound_at
) VALUES (?, ?, ?, ?)
""",
(
row["scope_type"],
row["scope_id"],
owner,
timestamp,
),
)
promoted += 1
connection.execute(
"""
DELETE FROM memory_scope_owner_candidates
WHERE scope_type = ? AND scope_id = ?
AND claimed_account_owner_id = ?
""",
(row["scope_type"], row["scope_id"], owner),
)
return promoted
def bind_run_memory_scopes(
self,
*,
project_id: str,
space_id: str,
account_owner_id: str,
) -> None:
for scope_type, scope_id in (
("project", project_id),
("space", space_id),
("user", account_owner_id),
):
self.bind_memory_scope_owner(
scope_type,
scope_id,
account_owner_id=account_owner_id,
)
def bind_memory_project_scopes(
self,
*,
project_id: str,
space_id: str,
user_id: str | None,
now: float | None = None,
) -> None:
"""Persist the shared Memory targets fed by one Project History.
This is routing metadata, not an ownership assertion. Cloud ownership
remains governed by ``memory_scope_owner_candidates`` and server-side
authorization.
"""
self._validate_memory_scope("project", project_id)
self._validate_memory_scope("space", space_id)
normalized_user_id = (
str(user_id).strip() if user_id is not None else None
)
if normalized_user_id:
self._validate_memory_scope("user", normalized_user_id)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
connection.execute(
"""
INSERT INTO memory_project_scope_bindings(
project_id, space_id, user_id, updated_at
) VALUES (?, ?, ?, ?)
ON CONFLICT(project_id) DO UPDATE SET
space_id = excluded.space_id,
user_id = excluded.user_id,
updated_at = excluded.updated_at
""",
(project_id, space_id, normalized_user_id, timestamp),
)
def get_memory_project_scopes(
self, project_id: str
) -> tuple[str | None, str | None]:
self._validate_memory_scope("project", project_id)
with self._lock:
row = self._connection.execute(
"""
SELECT space_id, user_id
FROM memory_project_scope_bindings
WHERE project_id = ?
""",
(project_id,),
).fetchone()
if row is None:
return None, None
return str(row["space_id"]), (
str(row["user_id"]) if row["user_id"] is not None else None
)
def get_memory_extraction_watermark(
self,
*,
target_scope_type: str,
target_scope_id: str,
source_project_id: str,
) -> str | None:
if target_scope_type not in {"space", "user"}:
raise ValueError("shared Memory extraction target is required")
self._validate_memory_scope(target_scope_type, target_scope_id)
self._validate_memory_scope("project", source_project_id)
with self._lock:
row = self._connection.execute(
"""
SELECT processed_through_watermark
FROM memory_extraction_watermarks
WHERE target_scope_type = ? AND target_scope_id = ?
AND source_project_id = ?
""",
(target_scope_type, target_scope_id, source_project_id),
).fetchone()
if row is None or row["processed_through_watermark"] is None:
return None
return str(row["processed_through_watermark"])
def record_memory_extraction_watermark(
self,
*,
target_scope_type: str,
target_scope_id: str,
source_project_id: str,
processed_through_watermark: str | None,
watermark_kind: str | None,
extractor_version: str,
last_error: str | None = None,
now: float | None = None,
) -> None:
"""Record one source Project's progress into a shared target scope."""
if target_scope_type not in {"space", "user"}:
raise ValueError("shared Memory extraction target is required")
self._validate_memory_scope(target_scope_type, target_scope_id)
self._validate_memory_scope("project", source_project_id)
if not extractor_version.strip():
raise ValueError("Memory extractor_version is required")
if last_error is None and (
processed_through_watermark is None or watermark_kind is None
):
raise ValueError("successful maintenance requires a watermark")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
connection.execute(
"""
INSERT INTO memory_extraction_watermarks(
target_scope_type, target_scope_id, source_project_id,
processed_through_watermark, watermark_kind,
extractor_version, last_error, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(
target_scope_type, target_scope_id, source_project_id
) DO UPDATE SET
processed_through_watermark = CASE
WHEN excluded.processed_through_watermark IS NULL
THEN memory_extraction_watermarks.processed_through_watermark
ELSE excluded.processed_through_watermark
END,
watermark_kind = CASE
WHEN excluded.watermark_kind IS NULL
THEN memory_extraction_watermarks.watermark_kind
ELSE excluded.watermark_kind
END,
extractor_version = excluded.extractor_version,
last_error = excluded.last_error,
updated_at = excluded.updated_at
""",
(
target_scope_type,
target_scope_id,
source_project_id,
processed_through_watermark,
watermark_kind,
extractor_version,
last_error[:2000] if last_error else None,
timestamp,
),
)
def get_memory_scope_state(
self, scope_type: str, scope_id: str
) -> MemoryScopeStateRecord | None:
self._validate_memory_scope(scope_type, scope_id)
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM memory_scope_state
WHERE scope_type = ? AND scope_id = ?
""",
(scope_type, scope_id),
).fetchone()
return (
self._memory_scope_state_from_row(row)
if row is not None
else None
)
def update_memory_scope_settings(
self,
scope_type: str,
scope_id: str,
*,
expected_revision: int,
capture_enabled: bool | None = None,
use_enabled: bool | None = None,
sync_scope: str | None = None,
now: float | None = None,
) -> MemoryScopeStateRecord:
self._validate_memory_scope(scope_type, scope_id)
if sync_scope is not None and sync_scope != "full_memory":
raise ValueError("Memory sync is fixed to full_memory")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = self._ensure_memory_scope_state_in_transaction(
connection,
scope_type=scope_type,
scope_id=scope_id,
owner_kind="desktop",
token_limit=_MEMORY_DEFAULT_TOKEN_LIMITS[scope_type],
now=timestamp,
)
if int(row["revision"]) != expected_revision:
raise OptimisticConcurrencyError(
f"Memory scope {scope_type}:{scope_id} expected revision "
f"{expected_revision}, found {row['revision']}"
)
updated = connection.execute(
"""
UPDATE memory_scope_state
SET capture_enabled = COALESCE(?, capture_enabled),
use_enabled = COALESCE(?, use_enabled),
sync_scope = COALESCE(?, sync_scope),
revision = revision + 1,
updated_at = ?
WHERE scope_type = ? AND scope_id = ? AND revision = ?
""",
(
int(capture_enabled)
if capture_enabled is not None
else None,
int(use_enabled) if use_enabled is not None else None,
sync_scope,
timestamp,
scope_type,
scope_id,
expected_revision,
),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
"Memory scope changed during update"
)
result = connection.execute(
"""
SELECT * FROM memory_scope_state
WHERE scope_type = ? AND scope_id = ?
""",
(scope_type, scope_id),
).fetchone()
assert result is not None
return self._memory_scope_state_from_row(result)
def advance_memory_snapshot_revision_after_cloud_conflict(
self,
scope_type: str,
scope_id: str,
*,
expected_revision: int,
now: float | None = None,
) -> bool:
"""Publish a changed local projection as a new immutable revision.
Older Desktop migrations changed snapshot-visible scope settings
without advancing ``revision``. Cloud correctly rejects those bytes
as a rewrite of an already stored revision. Repair only the scope
named by that explicit conflict, and use CAS so a concurrent real
Memory mutation always wins.
"""
self._validate_memory_scope(scope_type, scope_id)
if expected_revision < 0:
raise ValueError("expected Memory revision must be non-negative")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
updated = connection.execute(
"""
UPDATE memory_scope_state
SET revision = revision + 1, updated_at = ?
WHERE scope_type = ? AND scope_id = ? AND revision = ?
""",
(timestamp, scope_type, scope_id, expected_revision),
)
return updated.rowcount == 1
def get_memory_entry(self, memory_id: str) -> MemoryEntryRecord | None:
if not memory_id.strip():
raise ValueError("memory_id is required")
with self._lock:
row = self._connection.execute(
"SELECT * FROM memory_entries WHERE memory_id = ?",
(memory_id,),
).fetchone()
return (
self._memory_entry_from_row(row) if row is not None else None
)
def record_memory_maintenance_result(
self,
scope_type: str,
scope_id: str,
*,
expected_revision: int,
processed_through_watermark: str | None,
watermark_kind: str | None,
extractor_version: str,
last_error: str | None = None,
now: float | None = None,
) -> MemoryScopeStateRecord:
"""CAS-persist a bounded maintainer outcome.
A failed pass records only its error. A successful pass advances the
opaque History cursor after all idempotent mutations were applied.
"""
self._validate_memory_scope(scope_type, scope_id)
if not extractor_version.strip():
raise ValueError("Memory extractor_version is required")
if last_error is None and (
processed_through_watermark is None or watermark_kind is None
):
raise ValueError("successful maintenance requires a watermark")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = self._ensure_memory_scope_state_in_transaction(
connection,
scope_type=scope_type,
scope_id=scope_id,
owner_kind="desktop",
token_limit=_MEMORY_DEFAULT_TOKEN_LIMITS[scope_type],
now=timestamp,
)
if int(row["revision"]) != expected_revision:
raise OptimisticConcurrencyError(
f"Memory scope {scope_type}:{scope_id} expected revision "
f"{expected_revision}, found {row['revision']}"
)
updated = connection.execute(
"""
UPDATE memory_scope_state
SET processed_through_watermark = CASE
WHEN ? IS NULL THEN processed_through_watermark ELSE ? END,
watermark_kind = CASE
WHEN ? IS NULL THEN watermark_kind ELSE ? END,
extractor_version = ?, last_error = ?,
revision = revision + 1, updated_at = ?
WHERE scope_type = ? AND scope_id = ? AND revision = ?
""",
(
processed_through_watermark,
processed_through_watermark,
watermark_kind,
watermark_kind,
extractor_version,
last_error[:2000] if last_error else None,
timestamp,
scope_type,
scope_id,
expected_revision,
),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
"Memory scope changed during maintenance"
)
result = connection.execute(
"""SELECT * FROM memory_scope_state
WHERE scope_type = ? AND scope_id = ?""",
(scope_type, scope_id),
).fetchone()
assert result is not None
return self._memory_scope_state_from_row(result)
def record_memory_consolidation_result(
self,
scope_type: str,
scope_id: str,
*,
expected_revision: int,
now: float | None = None,
) -> MemoryScopeStateRecord:
"""CAS-record a completed bounded consolidation pass."""
self._validate_memory_scope(scope_type, scope_id)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = self._ensure_memory_scope_state_in_transaction(
connection,
scope_type=scope_type,
scope_id=scope_id,
owner_kind="desktop",
token_limit=_MEMORY_DEFAULT_TOKEN_LIMITS[scope_type],
now=timestamp,
)
if int(row["revision"]) != expected_revision:
raise OptimisticConcurrencyError(
f"Memory scope {scope_type}:{scope_id} expected revision "
f"{expected_revision}, found {row['revision']}"
)
updated = connection.execute(
"""
UPDATE memory_scope_state
SET last_consolidated_at = ?, last_error = NULL,
revision = revision + 1, updated_at = ?
WHERE scope_type = ? AND scope_id = ? AND revision = ?
""",
(
timestamp,
timestamp,
scope_type,
scope_id,
expected_revision,
),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
"Memory scope changed during consolidation"
)
result = connection.execute(
"""SELECT * FROM memory_scope_state
WHERE scope_type = ? AND scope_id = ?""",
(scope_type, scope_id),
).fetchone()
assert result is not None
return self._memory_scope_state_from_row(result)
def list_memory_entries(
self,
scope_type: str,
scope_id: str,
*,
include_deleted: bool = False,
limit: int = 200,
) -> list[MemoryEntryRecord]:
self._validate_memory_scope(scope_type, scope_id)
if limit < 1 or limit > 500:
raise ValueError("Memory list limit must be between 1 and 500")
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM memory_entries
WHERE scope_type = ? AND scope_id = ?
AND (? OR deleted_at IS NULL)
ORDER BY pinned_by_user DESC,
CASE priority WHEN 'high' THEN 0 ELSE 1 END,
updated_at DESC, memory_id
LIMIT ?
""",
(scope_type, scope_id, include_deleted, limit),
).fetchall()
return [self._memory_entry_from_row(row) for row in rows]
def list_memory_mutations(
self,
scope_type: str,
scope_id: str,
*,
memory_id: str | None = None,
limit: int = 100,
) -> list[MemoryMutationRecord]:
self._validate_memory_scope(scope_type, scope_id)
if limit < 1 or limit > 500:
raise ValueError("Memory mutation limit must be between 1 and 500")
query = """
SELECT * FROM memory_mutations
WHERE scope_type = ? AND scope_id = ?
"""
parameters: list[Any] = [scope_type, scope_id]
if memory_id is not None:
query += " AND memory_id = ?"
parameters.append(memory_id)
query += " ORDER BY created_at DESC, mutation_id DESC LIMIT ?"
parameters.append(limit)
with self._lock:
rows = self._connection.execute(query, parameters).fetchall()
return [self._memory_mutation_from_row(row) for row in rows]
def list_memory_reconciliation_items(
self,
scope_type: str,
scope_id: str,
*,
account_owner_id: str,
status: str = "pending",
limit: int = 100,
) -> list[MemoryReconciliationRecord]:
"""List explicit cross-device Memory conflicts for user review."""
self._validate_memory_scope(scope_type, scope_id)
if status not in {
"pending",
"accepted_local",
"accepted_cloud",
"dismissed",
}:
raise ValueError("invalid Memory reconciliation status")
if limit < 1 or limit > 200:
raise ValueError("Memory reconciliation limit is invalid")
owner = account_owner_id.strip()
if not owner:
raise ValueError("Memory account owner is required")
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM memory_reconciliation_items
WHERE scope_type = ? AND scope_id = ?
AND account_owner_id = ? AND status = ?
ORDER BY created_at, reconciliation_id
LIMIT ?
""",
(scope_type, scope_id, owner, status, limit),
).fetchall()
return [self._memory_reconciliation_from_row(row) for row in rows]
def get_memory_reconciliation_item(
self, reconciliation_id: str
) -> MemoryReconciliationRecord | None:
if not reconciliation_id.strip():
raise ValueError("Memory reconciliation id is required")
with self._lock:
row = self._connection.execute(
"""SELECT * FROM memory_reconciliation_items
WHERE reconciliation_id = ?""",
(reconciliation_id,),
).fetchone()
return (
self._memory_reconciliation_from_row(row)
if row is not None
else None
)
def resolve_memory_reconciliation_item(
self,
reconciliation_id: str,
*,
resolution: str,
now: float | None = None,
) -> MemoryReconciliationRecord:
"""CAS-close a conflict after the selected mutation was persisted."""
if resolution not in {
"accepted_local",
"accepted_cloud",
"dismissed",
}:
raise ValueError("invalid Memory reconciliation resolution")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
row = connection.execute(
"""SELECT * FROM memory_reconciliation_items
WHERE reconciliation_id = ?""",
(reconciliation_id,),
).fetchone()
if row is None:
raise RunNotFoundError("Memory reconciliation item not found")
if row["status"] != "pending":
if row["status"] != resolution:
raise IdempotencyConflictError(
"Memory reconciliation was already resolved differently"
)
return self._memory_reconciliation_from_row(row)
updated = connection.execute(
"""
UPDATE memory_reconciliation_items
SET status = ?, resolved_at = ?
WHERE reconciliation_id = ? AND status = 'pending'
""",
(resolution, timestamp, reconciliation_id),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
"Memory reconciliation changed during resolution"
)
result = connection.execute(
"""SELECT * FROM memory_reconciliation_items
WHERE reconciliation_id = ?""",
(reconciliation_id,),
).fetchone()
assert result is not None
return self._memory_reconciliation_from_row(result)
def apply_memory_mutation(
self,
*,
mutation_id: str,
idempotency_key: str,
operation: str,
scope_type: str,
scope_id: str,
memory_id: str | None,
actor_type: str,
reason: str,
content: str | None = None,
kind: str | None = None,
priority: str = "normal",
token_count: int | None = None,
created_by: str | None = None,
source_trust: str | None = None,
sensitivity: str = "normal",
source_refs: tuple[str, ...] = (),
expected_version: int | None = None,
actor_id: str | None = None,
run_id: str | None = None,
activity_id: str | None = None,
decision_id: str | None = None,
confirmed_by_user_action: bool = False,
reviewed_operation: str | None = None,
reviewed_memory_id: str | None = None,
now: float | None = None,
) -> MemoryMutationResult:
"""Apply one canonical, CAS-guarded lightweight Memory mutation."""
self._validate_memory_scope(scope_type, scope_id)
if not mutation_id.strip() or not idempotency_key.strip():
raise ValueError("mutation_id and idempotency_key are required")
if operation not in {
"add",
"replace",
"remove",
"restore",
"confirm",
"pin",
"consolidate",
"noop",
}:
raise ValueError("invalid Memory operation")
if actor_type not in {
"extractor",
"agent",
"user",
"importer",
"system",
}:
raise ValueError("invalid Memory actor_type")
if not reason.strip():
raise ValueError("Memory mutation reason is required")
if len(source_refs) > 32 or any(
len(value) > 256 for value in source_refs
):
raise ValueError("Memory source_refs exceed the bounded limit")
timestamp = now if now is not None else time.time()
encoded_refs = json.dumps(list(source_refs), separators=(",", ":"))
request_digest = hashlib.sha256(
json.dumps(
{
"mutation_id": mutation_id,
"memory_id": memory_id,
"scope_type": scope_type,
"scope_id": scope_id,
"operation": operation,
"actor_type": actor_type,
"reason": reason,
"content": content,
"kind": kind,
"priority": priority,
"token_count": token_count,
"created_by": created_by,
"source_trust": source_trust,
"sensitivity": sensitivity,
"source_refs": list(source_refs),
"expected_version": expected_version,
"actor_id": actor_id,
"run_id": run_id,
"activity_id": activity_id,
"decision_id": decision_id,
"reviewed_operation": reviewed_operation,
"reviewed_memory_id": reviewed_memory_id,
},
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
).encode("utf-8")
).hexdigest()
with self._write_transaction() as connection:
if confirmed_by_user_action:
self._assert_memory_user_decision_in_transaction(
connection,
decision_id=decision_id,
run_id=run_id,
memory_id=memory_id,
operation=operation,
reviewed_operation=reviewed_operation,
reviewed_memory_id=reviewed_memory_id,
)
duplicate = connection.execute(
"SELECT * FROM memory_mutations WHERE idempotency_key = ?",
(idempotency_key,),
).fetchone()
if duplicate is not None:
if (
duplicate["mutation_id"] != mutation_id
or duplicate["request_digest"] != request_digest
):
raise IdempotencyConflictError(
"Memory idempotency key was reused with different data"
)
entry_row = (
connection.execute(
"SELECT * FROM memory_entries WHERE memory_id = ?",
(memory_id,),
).fetchone()
if memory_id is not None
else None
)
state_row = self._ensure_memory_scope_state_in_transaction(
connection,
scope_type=scope_type,
scope_id=scope_id,
owner_kind="desktop",
token_limit=_MEMORY_DEFAULT_TOKEN_LIMITS[scope_type],
now=timestamp,
)
return MemoryMutationResult(
mutation=self._memory_mutation_from_row(duplicate),
entry=(
self._memory_entry_from_row(entry_row)
if entry_row is not None
else None
),
scope_state=self._memory_scope_state_from_row(state_row),
)
state_row = self._ensure_memory_scope_state_in_transaction(
connection,
scope_type=scope_type,
scope_id=scope_id,
owner_kind="desktop",
token_limit=_MEMORY_DEFAULT_TOKEN_LIMITS[scope_type],
now=timestamp,
)
entry_row = (
connection.execute(
"SELECT * FROM memory_entries WHERE memory_id = ?",
(memory_id,),
).fetchone()
if memory_id is not None
else None
)
before_hash = (
self._memory_entry_digest(entry_row)
if entry_row is not None
else None
)
token_delta = 0
if operation == "noop":
if memory_id is not None:
raise ValueError(
"noop mutation must not target a memory_id"
)
elif operation == "add":
if memory_id is None or entry_row is not None:
raise IdempotencyConflictError(
"add requires a new, explicit memory_id"
)
self._validate_memory_entry_values(
content=content,
kind=kind,
priority=priority,
token_count=token_count,
created_by=created_by,
source_trust=source_trust,
sensitivity=sensitivity,
)
token_delta = int(token_count or 0)
self._assert_memory_capacity(state_row, token_delta)
connection.execute(
"""
INSERT INTO memory_entries(
memory_id, scope_type, scope_id, kind, content,
priority, version, token_count, pinned_by_user,
confirmed_by_user, created_by, source_trust,
sensitivity, source_refs_json, usage_count,
created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, 1, ?, 0, ?, ?, ?, ?, ?, 0, ?, ?)
""",
(
memory_id,
scope_type,
scope_id,
kind,
content.strip() if content else content,
priority,
token_count,
int(actor_type == "user" or confirmed_by_user_action),
created_by,
source_trust,
sensitivity,
encoded_refs,
timestamp,
timestamp,
),
)
else:
if memory_id is None or entry_row is None:
raise RunNotFoundError("Memory entry does not exist")
if (
entry_row["scope_type"] != scope_type
or entry_row["scope_id"] != scope_id
):
raise IdempotencyConflictError(
"Memory entry belongs to a different scope"
)
if expected_version is None:
raise ValueError("expected_version is required")
if int(entry_row["version"]) != expected_version:
raise OptimisticConcurrencyError(
f"Memory entry expected version {expected_version}, "
f"found {entry_row['version']}"
)
was_active = entry_row["deleted_at"] is None
old_tokens = int(entry_row["token_count"]) if was_active else 0
if operation in {"replace", "consolidate"}:
if not was_active:
raise InvalidRunTransitionError(
"deleted Memory must be restored before replacement"
)
resolved_kind = kind or str(entry_row["kind"])
resolved_content = content or str(entry_row["content"])
resolved_tokens = token_count or int(
entry_row["token_count"]
)
resolved_created_by = created_by or str(
entry_row["created_by"]
)
resolved_trust = source_trust or str(
entry_row["source_trust"]
)
self._validate_memory_entry_values(
content=resolved_content,
kind=resolved_kind,
priority=priority,
token_count=resolved_tokens,
created_by=resolved_created_by,
source_trust=resolved_trust,
sensitivity=sensitivity,
)
token_delta = resolved_tokens - old_tokens
self._assert_memory_capacity(state_row, token_delta)
replacement_refs = (
encoded_refs
if source_refs
else str(entry_row["source_refs_json"])
)
connection.execute(
"""
UPDATE memory_entries
SET kind = ?, content = ?, priority = ?,
token_count = ?, created_by = ?, source_trust = ?,
sensitivity = ?, source_refs_json = ?,
confirmed_by_user = CASE
WHEN ? THEN 1 ELSE confirmed_by_user END,
version = version + 1, updated_at = ?
WHERE memory_id = ? AND version = ?
""",
(
resolved_kind,
resolved_content.strip(),
priority,
resolved_tokens,
resolved_created_by,
resolved_trust,
sensitivity,
replacement_refs,
int(
actor_type == "user"
or confirmed_by_user_action
),
timestamp,
memory_id,
expected_version,
),
)
elif operation == "remove":
if not was_active:
raise InvalidRunTransitionError(
"Memory is already deleted"
)
token_delta = -old_tokens
connection.execute(
"""
UPDATE memory_entries
SET deleted_at = ?, version = version + 1, updated_at = ?
WHERE memory_id = ? AND version = ?
""",
(timestamp, timestamp, memory_id, expected_version),
)
elif operation == "restore":
if was_active:
raise InvalidRunTransitionError(
"Memory is not deleted"
)
token_delta = int(entry_row["token_count"])
self._assert_memory_capacity(state_row, token_delta)
connection.execute(
"""
UPDATE memory_entries
SET deleted_at = NULL, version = version + 1, updated_at = ?
WHERE memory_id = ? AND version = ?
""",
(timestamp, memory_id, expected_version),
)
elif operation == "confirm":
if not was_active:
raise InvalidRunTransitionError(
"deleted Memory cannot be confirmed"
)
connection.execute(
"""
UPDATE memory_entries
SET confirmed_by_user = 1,
version = version + 1, updated_at = ?
WHERE memory_id = ? AND version = ?
""",
(timestamp, memory_id, expected_version),
)
elif operation == "pin":
if not was_active:
raise InvalidRunTransitionError(
"deleted Memory cannot be pinned"
)
connection.execute(
"""
UPDATE memory_entries
SET pinned_by_user = 1,
version = version + 1, updated_at = ?
WHERE memory_id = ? AND version = ?
""",
(timestamp, memory_id, expected_version),
)
if token_delta:
connection.execute(
"""
UPDATE memory_scope_state
SET current_token_count = current_token_count + ?
WHERE scope_type = ? AND scope_id = ?
""",
(token_delta, scope_type, scope_id),
)
connection.execute(
"""
UPDATE memory_scope_state
SET revision = revision + 1, updated_at = ?
WHERE scope_type = ? AND scope_id = ?
""",
(timestamp, scope_type, scope_id),
)
final_entry = (
connection.execute(
"SELECT * FROM memory_entries WHERE memory_id = ?",
(memory_id,),
).fetchone()
if memory_id is not None
else None
)
after_hash = (
self._memory_entry_digest(final_entry)
if final_entry is not None
else None
)
connection.execute(
"""
INSERT INTO memory_mutations(
mutation_id, memory_id, scope_type, scope_id, operation,
expected_version, before_hash, after_hash, actor_type,
actor_id, run_id, activity_id, reason, source_refs_json,
idempotency_key, request_digest, decision_id, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
mutation_id,
memory_id,
scope_type,
scope_id,
operation,
expected_version,
before_hash,
after_hash,
actor_type,
actor_id,
run_id,
activity_id,
reason,
encoded_refs,
idempotency_key,
request_digest,
decision_id,
timestamp,
),
)
state = connection.execute(
"""
SELECT * FROM memory_scope_state
WHERE scope_type = ? AND scope_id = ?
""",
(scope_type, scope_id),
).fetchone()
assert state is not None
if state["sync_scope"] == "full_memory":
entry_projection = None
if final_entry is not None:
entry_projection = {
"memory_id": final_entry["memory_id"],
"kind": final_entry["kind"],
"content": final_entry["content"],
"priority": final_entry["priority"],
"version": int(final_entry["version"]),
"token_count": int(final_entry["token_count"]),
"pinned_by_user": bool(final_entry["pinned_by_user"]),
"confirmed_by_user": bool(
final_entry["confirmed_by_user"]
),
"created_by": final_entry["created_by"],
"source_trust": final_entry["source_trust"],
"sensitivity": final_entry["sensitivity"],
"source_refs": json.loads(
final_entry["source_refs_json"]
),
"deleted_at": (
datetime.fromtimestamp(
float(final_entry["deleted_at"]), tz=UTC
).isoformat()
if final_entry["deleted_at"] is not None
else None
),
"created_at": datetime.fromtimestamp(
float(final_entry["created_at"]), tz=UTC
).isoformat(),
"updated_at": datetime.fromtimestamp(
float(final_entry["updated_at"]), tz=UTC
).isoformat(),
}
outbox_payload = {
"mutation_id": mutation_id,
"memory_id": memory_id,
"operation": operation,
"expected_version": expected_version,
"actor_type": actor_type,
"actor_id": actor_id,
"run_id": run_id,
"reason": reason,
"idempotency_key": idempotency_key,
"request_digest": request_digest,
"decision_id": decision_id,
"created_at": datetime.fromtimestamp(
timestamp, tz=UTC
).isoformat(),
"entry": entry_projection,
}
connection.execute(
"""
INSERT INTO memory_mutation_outbox(
mutation_id, scope_type, scope_id, status,
attempt_count, next_attempt_at, created_at, updated_at,
payload_json, scope_revision
) VALUES (?, ?, ?, 'pending', 0, ?, ?, ?, ?, ?)
""",
(
mutation_id,
scope_type,
scope_id,
timestamp,
timestamp,
timestamp,
json.dumps(
outbox_payload,
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
),
int(state["revision"]),
),
)
mutation_row = connection.execute(
"SELECT * FROM memory_mutations WHERE mutation_id = ?",
(mutation_id,),
).fetchone()
assert mutation_row is not None
return MemoryMutationResult(
mutation=self._memory_mutation_from_row(mutation_row),
entry=(
self._memory_entry_from_row(final_entry)
if final_entry is not None
else None
),
scope_state=self._memory_scope_state_from_row(state),
)
@staticmethod
def _assert_memory_user_decision_in_transaction(
connection: sqlite3.Connection,
*,
decision_id: str | None,
run_id: str | None,
memory_id: str | None,
operation: str,
reviewed_operation: str | None = None,
reviewed_memory_id: str | None = None,
) -> None:
if not decision_id or not run_id:
raise PermissionError(
"reviewed Memory mutation requires a durable user decision"
)
row = connection.execute(
"""
SELECT decisions.decision_json, decisions.actor_type,
interactions.run_id, interactions.request_json
FROM human_interaction_decisions AS decisions
JOIN human_interactions AS interactions
ON interactions.interaction_id = decisions.interaction_id
WHERE decisions.decision_id = ?
AND interactions.interaction_type = 'memory_change_review'
AND interactions.status = 'resolved'
""",
(decision_id,),
).fetchone()
if (
row is None
or row["actor_type"] != "user"
or row["run_id"] != run_id
):
raise PermissionError("Memory review was not approved by the user")
decision = json.loads(row["decision_json"])
request = json.loads(row["request_json"])
change = request.get("memory_change")
if (
decision.get("decision") != "approved"
or not isinstance(change, dict)
or change.get("operation") != (reviewed_operation or operation)
or (
(reviewed_memory_id or memory_id) is not None
and change.get("memory_id")
!= (reviewed_memory_id or memory_id)
)
):
raise PermissionError(
"Memory review does not authorize this exact mutation"
)
def get_run_final_result_event(
self, run_id: str
) -> CommittedRunEvent | None:
"""Return the canonical assistant result for one Run.
``assistant.final`` is preferred. The legacy fallback keeps Runs
created before schema v19 readable while callers migrate to the typed
contract.
"""
with self._lock:
row = self._connection.execute(
"""
SELECT event_id, run_id, sequence, run_version, event_type,
payload_json, legacy_step, created_at
FROM run_events
WHERE run_id = ?
AND (event_type = 'assistant.final' OR legacy_step = 'end')
ORDER BY CASE WHEN event_type = 'assistant.final' THEN 0 ELSE 1 END,
sequence DESC
LIMIT 1
""",
(run_id,),
).fetchone()
return self._event_from_row(row) if row is not None else None
def get_run_artifact_manifest_event(
self, run_id: str
) -> CommittedRunEvent | None:
"""Return the latest finalized canonical Artifact manifest."""
with self._lock:
row = self._connection.execute(
"""
SELECT event_id, run_id, sequence, run_version, event_type,
payload_json, legacy_step, created_at
FROM run_events
WHERE run_id = ?
AND event_type = 'artifact.manifest.finalized'
ORDER BY sequence DESC
LIMIT 1
""",
(run_id,),
).fetchone()
return self._event_from_row(row) if row is not None else None
def set_timeout_policy(
self,
run_id: str,
policy: RunTimeoutPolicy,
*,
now: float | None = None,
) -> RunRecord:
timestamp = now if now is not None else time.time()
encoded = json.dumps(
policy.to_dict(),
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
)
with self._write_transaction() as connection:
run = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
if run is None:
raise RunNotFoundError(f"run_id {run_id!r} does not exist")
connection.execute(
"""
UPDATE runs
SET timeout_policy_json = ?, timeout_policy_version = ?,
deadline_at = ?, updated_at = ?
WHERE run_id = ?
""",
(
encoded,
policy.policy_version,
policy.run_deadline_at,
timestamp,
run_id,
),
)
self._append_event_in_transaction(
connection,
run_id,
RunEventDraft(
event_id=f"timeout-policy:{run_id}:{policy.policy_version}",
event_type="run.timeout_policy_configured",
payload=policy.to_dict(),
created_at=timestamp,
),
)
row = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
assert row is not None
return self._run_from_row(row)
def create_run_attempt(
self,
run_id: str,
*,
request_id: str,
reason: str,
activate: bool = False,
attempt_id: str | None = None,
environment: AttemptEnvironmentBinding | None = None,
now: float | None = None,
) -> RunAttemptRecord:
if not request_id.strip() or not reason.strip():
raise ValueError("attempt request_id and reason are required")
timestamp = now if now is not None else time.time()
identifier = attempt_id or str(uuid.uuid4())
environment_values = self._attempt_environment_values(environment)
with self._write_transaction() as connection:
run = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
if run is None:
raise RunNotFoundError(f"run_id {run_id!r} does not exist")
duplicate = connection.execute(
"""
SELECT * FROM run_attempts
WHERE run_id = ? AND resume_request_id = ?
""",
(run_id, request_id),
).fetchone()
if duplicate is not None:
if duplicate["resume_reason"] != reason:
raise IdempotencyConflictError(
f"attempt request_id {request_id!r} was reused with a different reason"
)
persisted_environment = (
duplicate["environment_spec_id"],
duplicate["environment_spec_digest"],
duplicate["bundle_revision_id"],
duplicate["permission_profile_revision"],
duplicate["thinking_effort_requested"],
duplicate["thinking_effort_effective"],
duplicate["provider_capability_revision"],
)
if persisted_environment != environment_values:
raise IdempotencyConflictError(
f"attempt request_id {request_id!r} was reused with "
"a different environment"
)
# An idempotent row loaded from SQLite is audit/recovery data,
# not a fresh control-plane attestation. The original process
# already attested a genuinely in-process creation.
return self._attempt_from_row(duplicate)
if run["origin"] == "cloud_restore":
raise InvalidRunTransitionError(
f"run {run_id!r} was restored from Cloud without its local "
"workspace and execution context; start a new Run or fork it "
"after explicitly binding a workspace"
)
if run["status"] in {"completed", "failed", "cancelled"}:
raise InvalidRunTransitionError(
f"cannot create an attempt for terminal run {run_id!r}"
)
if run["cancel_request_id"] is not None:
raise InvalidRunTransitionError(
f"run {run_id!r} has a persisted cancel intent"
)
active = connection.execute(
"""
SELECT attempt_id FROM run_attempts
WHERE run_id = ? AND status IN ('pending', 'running', 'waiting_for_user')
LIMIT 1
""",
(run_id,),
).fetchone()
if active is not None:
raise InvalidRunTransitionError(
f"run {run_id!r} already has active attempt {active['attempt_id']!r}"
)
project_lease = connection.execute(
"""
SELECT run_id, attempt_id
FROM project_run_execution_leases
WHERE project_id = ?
""",
(run["project_id"],),
).fetchone()
if project_lease is not None:
if project_lease["run_id"] != run_id:
raise InvalidRunTransitionError(
f"project {run['project_id']!r} already executes Run "
f"{project_lease['run_id']!r}"
)
# A same-Run lease without an active Attempt is stale. This
# can only be left by a pre-v21 crash or manual DB repair;
# reclaim it inside the same writer transaction.
connection.execute(
"""DELETE FROM project_run_execution_leases
WHERE project_id = ? AND run_id = ?""",
(run["project_id"], run_id),
)
blockers = self._unsafe_resume_blockers(connection, run_id)
if blockers:
raise UnsafeResumeError(blockers)
unresolved_tools = connection.execute(
"""
SELECT calls.tool_call_id, calls.run_id
FROM tool_calls AS calls
JOIN runs AS owner ON owner.run_id = calls.run_id
WHERE owner.project_id = ?
AND calls.status IN ('prepared', 'dispatched')
ORDER BY calls.created_at
LIMIT 1
""",
(run["project_id"],),
).fetchone()
if unresolved_tools is not None:
raise InvalidRunTransitionError(
f"project {run['project_id']!r} still has unresolved Tool "
f"call {unresolved_tools['tool_call_id']!r} in Run "
f"{unresolved_tools['run_id']!r}"
)
pending_approvals = connection.execute(
"""
SELECT approval_id FROM approvals
WHERE run_id = ? AND status = 'pending'
ORDER BY created_at
""",
(run_id,),
).fetchall()
if pending_approvals:
raise InvalidRunTransitionError(
f"run {run_id!r} has pending approvals: "
+ ", ".join(
row["approval_id"] for row in pending_approvals
)
)
pending_interactions = connection.execute(
"""
SELECT interaction_id FROM human_interactions
WHERE run_id = ? AND interaction_type != 'approval'
AND status IN ('requested', 'presented')
ORDER BY created_at
""",
(run_id,),
).fetchall()
if pending_interactions:
raise InvalidRunTransitionError(
f"run {run_id!r} has pending human interactions: "
+ ", ".join(
row["interaction_id"] for row in pending_interactions
)
)
if environment is not None:
spec = connection.execute(
"""
SELECT * FROM effective_environment_specs
WHERE environment_spec_id = ?
""",
(environment.environment_spec_id,),
).fetchone()
if spec is None:
raise RunNotFoundError(
f"EnvironmentSpec "
f"{environment.environment_spec_id!r} does not exist"
)
expected_owner_id = (
run_id if spec["owner_type"] == "run" else identifier
)
if spec["owner_id"] != expected_owner_id:
raise IdempotencyConflictError(
"EnvironmentSpec belongs to another Run/Attempt"
)
persisted_spec_values = (
spec["environment_spec_digest"],
spec["bundle_revision_id"],
spec["permission_profile_revision"],
spec["provider_capability_revision"],
)
binding_spec_values = (
environment.environment_spec_digest,
environment.bundle_revision_id,
environment.permission_profile_revision,
environment.provider_capability_revision,
)
if persisted_spec_values != binding_spec_values:
raise IdempotencyConflictError(
"Attempt environment binding does not match its "
"immutable EnvironmentSpec"
)
number = int(
connection.execute(
"""
SELECT COALESCE(MAX(attempt_number), 0) + 1
FROM run_attempts WHERE run_id = ?
""",
(run_id,),
).fetchone()[0]
)
status = "running" if activate else "pending"
connection.execute(
"""
INSERT INTO run_attempts(
attempt_id, run_id, attempt_number, status, started_at,
ended_at, outcome, timeout_reason, resume_request_id,
resume_reason, policy_version, elapsed_active_ms,
last_consumer_heartbeat_at, environment_spec_id,
environment_spec_digest, bundle_revision_id,
permission_profile_revision, thinking_effort_requested,
thinking_effort_effective, provider_capability_revision
) VALUES (
?, ?, ?, ?, ?, NULL, NULL, NULL, ?, ?, ?, 0, ?,
?, ?, ?, ?, ?, ?, ?
)
""",
(
identifier,
run_id,
number,
status,
timestamp,
request_id,
reason,
run["timeout_policy_version"],
timestamp if activate else None,
*environment_values,
),
)
try:
connection.execute(
"""
INSERT INTO project_run_execution_leases(
project_id, run_id, attempt_id, acquired_at
) VALUES (?, ?, ?, ?)
""",
(run["project_id"], run_id, identifier, timestamp),
)
except sqlite3.IntegrityError as exc:
owner = connection.execute(
"""SELECT run_id FROM project_run_execution_leases
WHERE project_id = ?""",
(run["project_id"],),
).fetchone()
owner_id = owner["run_id"] if owner is not None else "unknown"
raise InvalidRunTransitionError(
f"project {run['project_id']!r} already executes Run "
f"{owner_id!r}"
) from exc
environment_payload = (
{
"environment_spec_id": environment.environment_spec_id,
"environment_spec_digest": (
environment.environment_spec_digest
),
"bundle_revision_id": environment.bundle_revision_id,
"permission_profile_revision": (
environment.permission_profile_revision
),
"thinking_effort_requested": (
environment.thinking_effort_requested
),
"thinking_effort_effective": (
environment.thinking_effort_effective
),
"provider_capability_revision": (
environment.provider_capability_revision
),
}
if environment is not None
else {}
)
self._append_event_in_transaction(
connection,
run_id,
RunEventDraft(
event_id=f"attempt:{identifier}:created",
event_type="run.attempt_created",
payload={
"attempt_id": identifier,
"attempt_number": number,
"reason": reason,
"status": status,
"policy_version": run["timeout_policy_version"],
**environment_payload,
},
created_at=timestamp,
),
run_status=status,
active_attempt_id=identifier,
)
# A follow-up's request_id is its deterministic Run id. Admit the
# durable queue row in the same transaction as its first Attempt,
# so a renderer crash cannot leave an executing Run queued forever.
connection.execute(
"""
UPDATE follow_up_requests
SET status = 'admitted', admitted_run_id = ?,
last_error = NULL, updated_at = ?
WHERE request_id = ? AND project_id = ? AND status = 'pending'
""",
(run_id, timestamp, run_id, run["project_id"]),
)
row = connection.execute(
"SELECT * FROM run_attempts WHERE attempt_id = ?",
(identifier,),
).fetchone()
assert row is not None
resolved = self._attempt_from_row(row)
self._trusted_attempt_permission_profiles.add(
(resolved.attempt_id, resolved.permission_profile_revision)
)
return resolved
def bind_pending_attempt_environment(
self,
attempt_id: str,
*,
run_id: str,
request_id: str,
environment: AttemptEnvironmentBinding,
now: float | None = None,
) -> RunAttemptRecord:
"""Attach the explicitly selected environment before an Attempt starts.
The Run control endpoint durably reserves a pending Resume Attempt
before ``/chat`` receives the fresh local credentials and Workspace
bindings needed to resolve an EnvironmentSpec. Legacy Runs can have no
prior spec to inherit, so Resume silently backfills that compatibility
snapshot exactly once. Running or terminal Attempts are immutable and
can never be rebound.
"""
timestamp = now if now is not None else time.time()
environment_values = self._attempt_environment_values(environment)
with self._write_transaction() as connection:
row = connection.execute(
"SELECT * FROM run_attempts WHERE attempt_id = ?",
(attempt_id,),
).fetchone()
if row is None:
raise RunNotFoundError(
f"attempt_id {attempt_id!r} does not exist"
)
if (
row["run_id"] != run_id
or row["resume_request_id"] != request_id
):
raise IdempotencyConflictError(
"pending Attempt does not match the Resume request"
)
persisted_environment = (
row["environment_spec_id"],
row["environment_spec_digest"],
row["bundle_revision_id"],
row["permission_profile_revision"],
row["thinking_effort_requested"],
row["thinking_effort_effective"],
row["provider_capability_revision"],
)
if row["environment_spec_id"] is not None:
if persisted_environment != environment_values:
raise IdempotencyConflictError(
"pending Attempt is already bound to a different environment"
)
return self._attempt_from_row(row)
if row["status"] != "pending":
raise InvalidRunTransitionError(
"only a pending Attempt can receive an environment binding"
)
spec = connection.execute(
"""
SELECT * FROM effective_environment_specs
WHERE environment_spec_id = ?
""",
(environment.environment_spec_id,),
).fetchone()
if spec is None:
raise RunNotFoundError(
f"EnvironmentSpec {environment.environment_spec_id!r} does not exist"
)
expected_owner_id = (
run_id if spec["owner_type"] == "run" else attempt_id
)
if spec["owner_id"] != expected_owner_id:
raise IdempotencyConflictError(
"EnvironmentSpec belongs to another Run/Attempt"
)
persisted_spec_values = (
spec["environment_spec_digest"],
spec["bundle_revision_id"],
spec["permission_profile_revision"],
spec["provider_capability_revision"],
)
binding_spec_values = (
environment.environment_spec_digest,
environment.bundle_revision_id,
environment.permission_profile_revision,
environment.provider_capability_revision,
)
if persisted_spec_values != binding_spec_values:
raise IdempotencyConflictError(
"Attempt environment binding does not match its immutable "
"EnvironmentSpec"
)
updated = connection.execute(
"""
UPDATE run_attempts
SET environment_spec_id = ?, environment_spec_digest = ?,
bundle_revision_id = ?, permission_profile_revision = ?,
thinking_effort_requested = ?, thinking_effort_effective = ?,
provider_capability_revision = ?
WHERE attempt_id = ? AND run_id = ? AND resume_request_id = ?
AND status = 'pending' AND environment_spec_id IS NULL
""",
(*environment_values, attempt_id, run_id, request_id),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
"pending Attempt changed while binding its environment"
)
self._append_event_in_transaction(
connection,
run_id,
RunEventDraft(
event_id=f"attempt:{attempt_id}:environment-bound",
event_type="run.attempt_environment_bound",
payload={
"attempt_id": attempt_id,
"environment_spec_id": environment.environment_spec_id,
"environment_spec_digest": (
environment.environment_spec_digest
),
"bundle_revision_id": environment.bundle_revision_id,
"permission_profile_revision": (
environment.permission_profile_revision
),
"thinking_effort_requested": (
environment.thinking_effort_requested
),
"thinking_effort_effective": (
environment.thinking_effort_effective
),
"provider_capability_revision": (
environment.provider_capability_revision
),
"reason": "legacy_environment_backfill",
},
created_at=timestamp,
),
run_status="pending",
active_attempt_id=attempt_id,
)
rebound = connection.execute(
"SELECT * FROM run_attempts WHERE attempt_id = ?",
(attempt_id,),
).fetchone()
assert rebound is not None
resolved = self._attempt_from_row(rebound)
self._trusted_attempt_permission_profiles.add(
(resolved.attempt_id, resolved.permission_profile_revision)
)
return resolved
def get_run_attempt(self, attempt_id: str) -> RunAttemptRecord | None:
with self._lock:
row = self._connection.execute(
"SELECT * FROM run_attempts WHERE attempt_id = ?",
(attempt_id,),
).fetchone()
return self._attempt_from_row(row) if row is not None else None
def attempt_permission_profile_is_trusted(
self,
attempt_id: str,
permission_profile_revision: str | None,
) -> bool:
return (attempt_id, permission_profile_revision) in (
self._trusted_attempt_permission_profiles
)
def list_run_attempts(self, run_id: str) -> list[RunAttemptRecord]:
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM run_attempts
WHERE run_id = ? ORDER BY attempt_number
""",
(run_id,),
).fetchall()
return [self._attempt_from_row(row) for row in rows]
def heartbeat_attempt(
self,
attempt_id: str,
*,
expected_run_id: str | None = None,
now: float | None = None,
) -> RunAttemptRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
updated = connection.execute(
"""
UPDATE run_attempts
SET elapsed_active_ms = elapsed_active_ms + CAST(
MAX(0, ? - COALESCE(last_consumer_heartbeat_at, ?)) * 1000
AS INTEGER
),
last_consumer_heartbeat_at = ?
WHERE attempt_id = ? AND status = 'running'
""",
(timestamp, timestamp, timestamp, attempt_id),
)
if updated.rowcount != 1:
raise InvalidRunTransitionError(
f"attempt {attempt_id!r} is not running"
)
row = connection.execute(
"SELECT * FROM run_attempts WHERE attempt_id = ?",
(attempt_id,),
).fetchone()
assert row is not None
if (
expected_run_id is not None
and row["run_id"] != expected_run_id
):
raise IdempotencyConflictError(
f"attempt {attempt_id!r} does not belong to run {expected_run_id!r}"
)
return self._attempt_from_row(row)
def activate_run_attempt(
self,
attempt_id: str,
*,
expected_run_id: str | None = None,
now: float | None = None,
) -> RunAttemptRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
attempt = connection.execute(
"SELECT * FROM run_attempts WHERE attempt_id = ?",
(attempt_id,),
).fetchone()
if attempt is None:
raise RunNotFoundError(
f"attempt_id {attempt_id!r} does not exist"
)
if (
expected_run_id is not None
and attempt["run_id"] != expected_run_id
):
raise IdempotencyConflictError(
f"attempt {attempt_id!r} does not belong to run {expected_run_id!r}"
)
if attempt["status"] == "running":
return self._attempt_from_row(attempt)
if not transition_allowed(
ATTEMPT_TRANSITIONS,
str(attempt["status"]),
"running",
):
raise InvalidRunTransitionError(
f"attempt {attempt_id!r} is not pending"
)
connection.execute(
"""
UPDATE run_attempts
SET status = 'running', last_consumer_heartbeat_at = ?
WHERE attempt_id = ? AND status = 'pending'
""",
(timestamp, attempt_id),
)
self._append_event_in_transaction(
connection,
attempt["run_id"],
RunEventDraft(
event_id=f"attempt:{attempt_id}:started",
event_type="run.attempt_started",
payload={
"attempt_id": attempt_id,
"attempt_number": int(attempt["attempt_number"]),
"policy_version": attempt["policy_version"],
},
created_at=timestamp,
),
run_status="running",
active_attempt_id=attempt_id,
)
row = connection.execute(
"SELECT * FROM run_attempts WHERE attempt_id = ?",
(attempt_id,),
).fetchone()
assert row is not None
return self._attempt_from_row(row)
def fork_run(
self,
source_run_id: str,
*,
new_run_id: str,
request_id: str,
now: float | None = None,
) -> tuple[RunRecord, RunAttemptRecord]:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
existing = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (new_run_id,)
).fetchone()
if existing is not None:
if existing["parent_run_id"] != source_run_id:
raise IdempotencyConflictError(
f"fork target {new_run_id!r} already exists"
)
attempts = connection.execute(
"SELECT * FROM run_attempts WHERE run_id = ? ORDER BY attempt_number",
(new_run_id,),
).fetchall()
if not attempts:
raise IdempotencyConflictError(
"fork is missing its checkpoint attempt"
)
if attempts[0]["resume_request_id"] != request_id:
raise IdempotencyConflictError(
f"fork target {new_run_id!r} was created by another request"
)
return self._run_from_row(existing), self._attempt_from_row(
attempts[0]
)
source = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (source_run_id,)
).fetchone()
if source is None:
raise RunNotFoundError(
f"run_id {source_run_id!r} does not exist"
)
attempt_id = str(uuid.uuid4())
inherited_policy = json.loads(
source["timeout_policy_json"] or "{}"
)
inherited_policy["run_deadline_at"] = None
connection.execute(
"""
INSERT INTO runs(
run_id, project_id, status, version, active_attempt_id,
deadline_at, timeout_policy_version, created_at, updated_at,
parent_run_id, timeout_policy_json
) VALUES (?, ?, 'interrupted', 0, NULL, ?, ?, ?, ?, ?, ?)
""",
(
new_run_id,
source["project_id"],
None,
source["timeout_policy_version"],
timestamp,
timestamp,
source_run_id,
json.dumps(
inherited_policy,
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
),
),
)
connection.execute(
"""
INSERT INTO run_attempts(
attempt_id, run_id, attempt_number, status, started_at,
ended_at, outcome, timeout_reason, resume_request_id,
resume_reason, policy_version, elapsed_active_ms
) VALUES (?, ?, 1, 'interrupted', ?, ?, 'fork_checkpoint',
NULL, ?, 'fork', ?, 0)
""",
(
attempt_id,
new_run_id,
timestamp,
timestamp,
request_id,
source["timeout_policy_version"],
),
)
self._append_event_in_transaction(
connection,
new_run_id,
RunEventDraft(
event_id=f"fork:{new_run_id}:{request_id}",
event_type="run.forked",
payload={
"source_run_id": source_run_id,
"checkpoint_attempt_id": attempt_id,
"requires_resume": True,
},
created_at=timestamp,
),
run_status="interrupted",
)
run = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (new_run_id,)
).fetchone()
attempt = connection.execute(
"SELECT * FROM run_attempts WHERE attempt_id = ?",
(attempt_id,),
).fetchone()
assert run is not None and attempt is not None
return self._run_from_row(run), self._attempt_from_row(attempt)
def request_cancel(
self,
run_id: str,
*,
request_id: str,
reason: str,
now: float | None = None,
) -> RunRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
run = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
if run is None:
raise RunNotFoundError(f"run_id {run_id!r} does not exist")
if run["status"] == "cancelled":
return self._run_from_row(run)
if run["status"] in {"completed", "failed"}:
raise InvalidRunTransitionError(
f"cannot cancel terminal run {run_id!r}"
)
if run["cancel_request_id"] not in {None, request_id}:
raise InvalidRunTransitionError(
f"run {run_id!r} already has another cancel intent"
)
if run["cancel_request_id"] is not None:
event = connection.execute(
"SELECT payload_json FROM run_events WHERE event_id = ?",
(f"cancel:{request_id}:requested",),
).fetchone()
if (
event is None
or json.loads(event["payload_json"]).get("reason")
!= reason
):
raise IdempotencyConflictError(
f"cancel request_id {request_id!r} was reused with different data"
)
else:
connection.execute(
"""
UPDATE runs SET cancel_request_id = ?, cancel_requested_at = ?
WHERE run_id = ?
""",
(request_id, timestamp, run_id),
)
self._append_event_in_transaction(
connection,
run_id,
RunEventDraft(
event_id=f"cancel:{request_id}:requested",
event_type="run.cancel_requested",
payload={"request_id": request_id, "reason": reason},
created_at=timestamp,
),
)
row = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
assert row is not None
return self._run_from_row(row)
def complete_cancel(
self,
run_id: str,
*,
request_id: str,
now: float | None = None,
) -> RunRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
run = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
if run is None:
raise RunNotFoundError(f"run_id {run_id!r} does not exist")
if run["status"] == "cancelled":
return self._run_from_row(run)
if run["cancel_request_id"] != request_id:
raise InvalidRunTransitionError(
"cancel completion has no matching intent"
)
connection.execute(
"""
UPDATE run_attempts
SET status = 'cancelled', ended_at = COALESCE(ended_at, ?),
outcome = COALESCE(outcome, 'explicit_cancel')
WHERE run_id = ? AND status IN ('pending', 'running', 'waiting_for_user')
""",
(timestamp, run_id),
)
dispatched_tools = connection.execute(
"""
SELECT * FROM tool_calls
WHERE run_id = ? AND status = 'dispatched'
AND outcome IS NULL
ORDER BY created_at, tool_call_id
""",
(run_id,),
).fetchall()
for tool in dispatched_tools:
connection.execute(
"""
UPDATE tool_calls
SET status = 'outcome_unknown',
outcome = 'outcome_unknown', updated_at = ?
WHERE tool_call_id = ? AND status = 'dispatched'
""",
(timestamp, tool["tool_call_id"]),
)
self._append_event_in_transaction(
connection,
run_id,
RunEventDraft(
event_id=(
"cancel:tool-outcome-unknown:"
f"{tool['tool_call_id']}"
),
event_type="tool.outcome_unknown",
payload={
"tool_call_id": tool["tool_call_id"],
"safety_class": tool["safety_class"],
"reason": "cancelled_after_dispatch",
},
created_at=timestamp,
),
)
payload: dict[str, Any] = {
"request_id": request_id,
"reason": "explicit_cancel",
}
manifest = connection.execute(
"""
SELECT * FROM run_events
WHERE run_id = ?
AND event_type = 'artifact.manifest.finalized'
ORDER BY sequence DESC LIMIT 1
""",
(run_id,),
).fetchone()
if manifest is not None:
manifest_event = self._event_from_row(manifest)
payload.update(
artifact_manifest_event_id=manifest_event.event_id,
artifact_count=int(
manifest_event.payload.get("artifact_count", 0)
),
)
self._append_event_in_transaction(
connection,
run_id,
RunEventDraft(
event_id=f"cancel:{request_id}:completed",
event_type="run.cancelled",
payload=payload,
created_at=timestamp,
),
run_status="cancelled",
clear_active_attempt=True,
)
row = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
assert row is not None
return self._run_from_row(row)
def checkpoint_tool_call(
self,
*,
tool_call_id: str,
run_id: str,
attempt_id: str | None,
tool_name: str,
safety_class: ToolSafetyClass,
status: str,
request: dict[str, Any] | None = None,
result: dict[str, Any] | None = None,
idempotency_key: str | None = None,
outcome: str | None = None,
timeout_reason: str | None = None,
now: float | None = None,
) -> ToolCallRecord:
if status not in {
value for allowed in TOOL_TRANSITIONS.values() for value in allowed
}:
raise ValueError("unsupported tool checkpoint status")
if (
safety_class is ToolSafetyClass.IDEMPOTENT_WRITE
and not idempotency_key
):
raise ValueError("idempotent writes require an idempotency key")
timestamp = now if now is not None else time.time()
request_json = json.dumps(
request or {},
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
)
result_json = (
json.dumps(
result,
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
)
if result is not None
else None
)
with self._write_transaction() as connection:
run = connection.execute(
"SELECT run_id FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
if run is None:
raise RunNotFoundError(f"run_id {run_id!r} does not exist")
existing = connection.execute(
"SELECT * FROM tool_calls WHERE tool_call_id = ?",
(tool_call_id,),
).fetchone()
if attempt_id is not None:
attempt = connection.execute(
"SELECT run_id FROM run_attempts WHERE attempt_id = ?",
(attempt_id,),
).fetchone()
if attempt is None or attempt["run_id"] != run_id:
raise IdempotencyConflictError(
f"attempt {attempt_id!r} does not belong to run {run_id!r}"
)
previous = existing["status"] if existing is not None else None
if not transition_allowed(TOOL_TRANSITIONS, previous, status):
raise InvalidRunTransitionError(
f"tool call {tool_call_id!r} cannot move from {previous!r} to {status!r}"
)
if (
status == "timed_out"
and safety_class is ToolSafetyClass.UNSAFE_WRITE
):
raise InvalidRunTransitionError(
f"unsafe write tool {tool_call_id!r} must enter outcome_unknown, "
"not timed_out"
)
if existing is not None and (
existing["run_id"] != run_id
or existing["tool_name"] != tool_name
or existing["safety_class"] != safety_class.value
or existing["idempotency_key"] != idempotency_key
or existing["request_json"] != request_json
):
raise IdempotencyConflictError(
f"tool_call_id {tool_call_id!r} was reused with different data"
)
if existing is None:
connection.execute(
"""
INSERT INTO tool_calls(
tool_call_id, run_id, attempt_id, tool_name, status,
safety_class, idempotency_key, outcome, timeout_reason,
created_at, updated_at, request_json, result_json,
prepared_at, dispatched_at, completed_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL)
""",
(
tool_call_id,
run_id,
attempt_id,
tool_name,
status,
safety_class.value,
idempotency_key,
outcome,
timeout_reason,
timestamp,
timestamp,
request_json,
result_json,
timestamp,
),
)
elif status != previous or result_json != existing["result_json"]:
connection.execute(
"""
UPDATE tool_calls
SET status = ?, result_json = COALESCE(?, result_json),
outcome = COALESCE(?, outcome),
timeout_reason = COALESCE(?, timeout_reason),
dispatched_at = CASE WHEN ? = 'dispatched'
THEN COALESCE(dispatched_at, ?) ELSE dispatched_at END,
completed_at = CASE WHEN ? IN ('completed', 'failed')
THEN COALESCE(completed_at, ?) ELSE completed_at END,
updated_at = ?
WHERE tool_call_id = ?
""",
(
status,
result_json,
outcome,
timeout_reason,
status,
timestamp,
status,
timestamp,
timestamp,
tool_call_id,
),
)
event_type = f"tool.{status}"
event_id = f"tool:{tool_call_id}:{status}"
payload = {
"tool_call_id": tool_call_id,
"attempt_id": attempt_id,
"tool_name": tool_name,
"safety_class": safety_class.value,
"status": status,
"outcome": outcome,
"timeout_reason": timeout_reason,
"request": request or {},
"result": result,
}
self._append_event_in_transaction(
connection,
run_id,
RunEventDraft(
event_id=event_id,
event_type=event_type,
payload=payload,
created_at=timestamp,
),
)
row = connection.execute(
"SELECT * FROM tool_calls WHERE tool_call_id = ?",
(tool_call_id,),
).fetchone()
assert row is not None
return self._tool_call_from_row(row)
def list_tool_calls(self, run_id: str) -> list[ToolCallRecord]:
with self._lock:
rows = self._connection.execute(
"SELECT * FROM tool_calls WHERE run_id = ? ORDER BY created_at",
(run_id,),
).fetchall()
return [self._tool_call_from_row(row) for row in rows]
def create_human_interaction(
self,
*,
interaction_id: str,
run_id: str,
attempt_id: str | None,
interaction_type: str,
request: dict[str, Any],
response_schema: dict[str, Any] | None = None,
options: list[dict[str, Any]] | None = None,
requested_by: str = "agent",
expires_at: float | None = None,
now: float | None = None,
) -> HumanInteractionRecord:
if interaction_type not in {
"question",
"choice",
"form",
"confirmation",
"diff_review",
"merge_conflict",
"credential_binding",
"memory_change_review",
}:
if interaction_type == "approval":
raise ValueError(
"approval interactions must be created by create_approval"
)
raise ValueError(
f"unsupported interaction type {interaction_type!r}"
)
timestamp = now if now is not None else time.time()
request_json = canonical_json(request)
response_schema_json = canonical_json(response_schema or {})
normalized_options = list(options or [])
option_rows: list[tuple[str, int, str, str, str | None]] = []
seen_option_ids: set[str] = set()
for position, option in enumerate(normalized_options):
option_id = str(option.get("option_id") or option.get("id") or "")
label = str(option.get("label") or "")
if not option_id or not label or option_id in seen_option_ids:
raise ValueError(
"interaction options require unique ids and labels"
)
seen_option_ids.add(option_id)
option_rows.append(
(
option_id,
position,
label,
canonical_json(option.get("value", option_id)),
(
str(option["description"])
if option.get("description") is not None
else None
),
)
)
with self._write_transaction() as connection:
existing = connection.execute(
"SELECT * FROM human_interactions WHERE interaction_id = ?",
(interaction_id,),
).fetchone()
if existing is not None:
existing_options = connection.execute(
"""
SELECT option_id, position, label, value_json, description
FROM human_interaction_options
WHERE interaction_id = ? ORDER BY position
""",
(interaction_id,),
).fetchall()
if (
existing["run_id"] != run_id
or existing["attempt_id"] != attempt_id
or existing["interaction_type"] != interaction_type
or existing["request_json"] != request_json
or existing["response_schema_json"] != response_schema_json
or existing["requested_by"] != requested_by
or existing["expires_at"] != expires_at
or [tuple(row) for row in existing_options] != option_rows
):
raise IdempotencyConflictError(
f"interaction_id {interaction_id!r} was reused"
)
return self._human_interaction_from_row(existing)
run = connection.execute(
"SELECT status, active_attempt_id FROM runs WHERE run_id = ?",
(run_id,),
).fetchone()
if run is None:
raise RunNotFoundError(f"run_id {run_id!r} does not exist")
if run["status"] in {"completed", "failed", "cancelled"}:
raise InvalidRunTransitionError(
f"terminal run {run_id!r} cannot request human interaction"
)
self._validate_waiting_attempt(
connection,
run_id=run_id,
attempt_id=attempt_id,
active_attempt_id=run["active_attempt_id"],
interaction_label="human interaction",
)
connection.execute(
"""
INSERT INTO human_interactions(
interaction_id, run_id, attempt_id, interaction_type,
status, request_json, response_schema_json, requested_by,
version, expires_at, presented_at, resolved_at,
created_at, updated_at
) VALUES (?, ?, ?, ?, 'requested', ?, ?, ?, 0, ?, NULL, NULL, ?, ?)
""",
(
interaction_id,
run_id,
attempt_id,
interaction_type,
request_json,
response_schema_json,
requested_by,
expires_at,
timestamp,
timestamp,
),
)
connection.executemany(
"""
INSERT INTO human_interaction_options(
interaction_id, option_id, position, label, value_json,
description
) VALUES (?, ?, ?, ?, ?, ?)
""",
[(interaction_id, *row) for row in option_rows],
)
if attempt_id is not None:
updated_attempt = connection.execute(
"""
UPDATE run_attempts SET status = 'waiting_for_user'
WHERE attempt_id = ? AND status = 'running'
""",
(attempt_id,),
)
if updated_attempt.rowcount != 1:
raise InvalidRunTransitionError(
f"interaction attempt {attempt_id!r} is no longer running"
)
self._append_event_in_transaction(
connection,
run_id,
RunEventDraft(
event_id=f"interaction:{interaction_id}:requested",
event_type="interaction.requested",
payload={
"interaction_id": interaction_id,
"version": 0,
"interaction_type": interaction_type,
"attempt_id": attempt_id,
"request": request,
"response_schema": response_schema or {},
"options": normalized_options,
"requested_by": requested_by,
"expires_at": expires_at,
},
created_at=timestamp,
),
run_status="waiting_for_user",
active_attempt_id=attempt_id,
)
row = connection.execute(
"SELECT * FROM human_interactions WHERE interaction_id = ?",
(interaction_id,),
).fetchone()
assert row is not None
return self._human_interaction_from_row(row)
def resolve_human_interaction(
self,
interaction_id: str,
*,
decision_request_id: str,
decision: dict[str, Any],
expected_version: int,
expected_run_id: str | None = None,
actor_type: str = "user",
actor_id: str | None = None,
source: str = "desktop",
continue_active_attempt: bool = False,
now: float | None = None,
) -> HumanInteractionRecord:
if not decision_request_id:
raise ValueError("decision_request_id is required")
if actor_type not in {"user", "auto_reviewer", "system"}:
raise ValueError("invalid interaction decision actor_type")
if source not in {"desktop", "remote_control", "recovery", "expiry"}:
raise ValueError("invalid interaction decision source")
timestamp = now if now is not None else time.time()
decision_json = canonical_json(decision)
with self._write_transaction() as connection:
interaction = connection.execute(
"SELECT * FROM human_interactions WHERE interaction_id = ?",
(interaction_id,),
).fetchone()
if interaction is None:
raise RunNotFoundError(
f"interaction_id {interaction_id!r} does not exist"
)
if interaction["interaction_type"] == "approval":
raise InvalidRunTransitionError(
"approval interactions must be resolved by decide_approval"
)
if (
expected_run_id is not None
and interaction["run_id"] != expected_run_id
):
raise IdempotencyConflictError(
f"interaction {interaction_id!r} does not belong to run "
f"{expected_run_id!r}"
)
duplicate = connection.execute(
"""
SELECT * FROM human_interaction_decisions
WHERE interaction_id = ? AND decision_request_id = ?
""",
(interaction_id, decision_request_id),
).fetchone()
if duplicate is not None:
if (
duplicate["decision_json"] != decision_json
or duplicate["actor_type"] != actor_type
or duplicate["actor_id"] != actor_id
or duplicate["source"] != source
):
raise IdempotencyConflictError(
f"decision_request_id {decision_request_id!r} was reused"
)
return self._human_interaction_from_row(interaction)
if interaction["status"] not in {"requested", "presented"}:
raise InvalidRunTransitionError(
f"interaction {interaction_id!r} is already "
f"{interaction['status']}"
)
if int(interaction["version"]) != expected_version:
raise OptimisticConcurrencyError(
f"interaction {interaction_id!r} expected version "
f"{expected_version}"
)
can_continue = self._resolve_waiting_attempt(
connection,
attempt_id=interaction["attempt_id"],
continue_active_attempt=continue_active_attempt,
timestamp=timestamp,
outcome="human_interaction_resolved",
)
connection.execute(
"""
INSERT INTO human_interaction_decisions(
decision_id, interaction_id, decision_request_id,
decision_json, actor_type, actor_id, source,
action_digest, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, NULL, ?)
""",
(
str(uuid.uuid4()),
interaction_id,
decision_request_id,
decision_json,
actor_type,
actor_id,
source,
timestamp,
),
)
updated = connection.execute(
"""
UPDATE human_interactions
SET status = 'resolved', resolved_at = ?, updated_at = ?,
version = version + 1
WHERE interaction_id = ? AND version = ?
AND status IN ('requested', 'presented')
""",
(timestamp, timestamp, interaction_id, expected_version),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
f"interaction {interaction_id!r} changed while resolving"
)
connection.execute(
"""
INSERT OR IGNORE INTO security_audit_events(
audit_event_id, space_id, run_id, interaction_id,
event_type, actor_type, actor_id, action_digest,
details_json, created_at
) VALUES (?, NULL, ?, ?, ?, ?, ?, NULL, ?, ?)
""",
(
f"interaction-decision:{interaction_id}:{decision_request_id}",
interaction["run_id"],
interaction_id,
"human_interaction.resolved",
actor_type,
actor_id,
canonical_json(
{
"interaction_type": interaction[
"interaction_type"
],
"source": source,
"decision_fields": sorted(decision),
}
),
timestamp,
),
)
self._append_event_in_transaction(
connection,
interaction["run_id"],
RunEventDraft(
event_id=(
f"interaction:{interaction_id}:decision:"
f"{decision_request_id}"
),
event_type="interaction.resolved",
payload={
"interaction_id": interaction_id,
"interaction_type": interaction["interaction_type"],
"decision_request_id": decision_request_id,
"decision": decision,
"actor_type": actor_type,
"actor_id": actor_id,
"source": source,
"continued_attempt": can_continue,
},
created_at=timestamp,
),
run_status="running" if can_continue else "interrupted",
active_attempt_id=(
interaction["attempt_id"] if can_continue else None
),
clear_active_attempt=not can_continue,
)
row = connection.execute(
"SELECT * FROM human_interactions WHERE interaction_id = ?",
(interaction_id,),
).fetchone()
assert row is not None
return self._human_interaction_from_row(row)
def get_human_interaction(
self, interaction_id: str
) -> HumanInteractionRecord | None:
with self._lock:
row = self._connection.execute(
"SELECT * FROM human_interactions WHERE interaction_id = ?",
(interaction_id,),
).fetchone()
return self._human_interaction_from_row(row) if row else None
def list_human_interactions(
self, run_id: str, *, pending_only: bool = False
) -> list[HumanInteractionRecord]:
query = "SELECT * FROM human_interactions WHERE run_id = ?"
if pending_only:
query += " AND status IN ('requested', 'presented')"
query += " ORDER BY created_at, interaction_id"
with self._lock:
rows = self._connection.execute(query, (run_id,)).fetchall()
return [self._human_interaction_from_row(row) for row in rows]
def list_human_interaction_options(
self, interaction_id: str
) -> list[HumanInteractionOptionRecord]:
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM human_interaction_options
WHERE interaction_id = ? ORDER BY position
""",
(interaction_id,),
).fetchall()
return [
self._human_interaction_option_from_row(row) for row in rows
]
def list_human_interaction_decisions(
self, interaction_id: str
) -> list[HumanInteractionDecisionRecord]:
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM human_interaction_decisions
WHERE interaction_id = ? ORDER BY created_at, decision_id
""",
(interaction_id,),
).fetchall()
return [
self._human_interaction_decision_from_row(row) for row in rows
]
def get_space_permission_profile(
self, space_id: str
) -> SpacePermissionProfileRecord | None:
with self._lock:
row = self._connection.execute(
"SELECT * FROM space_permission_profiles WHERE space_id = ?",
(space_id,),
).fetchone()
return (
self._space_permission_profile_from_row(row) if row else None
)
def get_space_permission_profile_revision(
self,
revision_id: str,
) -> SpacePermissionProfileRevisionRecord | None:
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM space_permission_profile_revisions
WHERE revision_id = ?
""",
(revision_id,),
).fetchone()
return (
self._space_permission_profile_revision_from_row(row)
if row
else None
)
def put_space_permission_profile(
self,
*,
space_id: str,
profile_name: str,
sandbox_mode: str,
approval_mode: str,
reviewer_mode: str,
updated_by: str,
expected_revision: int | None = None,
audit_request_id: str | None = None,
now: float | None = None,
) -> SpacePermissionProfileRecord:
if profile_name not in {
"read_only",
"request_approval",
"auto_reviewer",
"full_access",
}:
raise ValueError("invalid permission profile name")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
existing = connection.execute(
"SELECT * FROM space_permission_profiles WHERE space_id = ?",
(space_id,),
).fetchone()
values = (
profile_name,
sandbox_mode,
approval_mode,
reviewer_mode,
updated_by,
)
if existing is None:
if expected_revision not in {None, 0}:
raise OptimisticConcurrencyError(
f"space {space_id!r} has no permission profile at "
f"revision {expected_revision}"
)
connection.execute(
"""
INSERT INTO space_permission_profiles(
space_id, profile_name, sandbox_mode, approval_mode,
reviewer_mode, revision, updated_by, created_at,
updated_at
) VALUES (?, ?, ?, ?, ?, 1, ?, ?, ?)
""",
(space_id, *values, timestamp, timestamp),
)
next_revision = 1
else:
current_values = (
existing["profile_name"],
existing["sandbox_mode"],
existing["approval_mode"],
existing["reviewer_mode"],
existing["updated_by"],
)
if current_values == values:
next_revision = int(existing["revision"])
else:
if (
expected_revision is not None
and int(existing["revision"]) != expected_revision
):
raise OptimisticConcurrencyError(
f"space {space_id!r} permission profile expected "
f"revision {expected_revision}"
)
connection.execute(
"""
UPDATE space_permission_profiles
SET profile_name = ?, sandbox_mode = ?, approval_mode = ?,
reviewer_mode = ?, revision = revision + 1,
updated_by = ?, updated_at = ?
WHERE space_id = ? AND revision = ?
""",
(
profile_name,
sandbox_mode,
approval_mode,
reviewer_mode,
updated_by,
timestamp,
space_id,
int(existing["revision"]),
),
)
next_revision = int(existing["revision"]) + 1
row = connection.execute(
"SELECT * FROM space_permission_profiles WHERE space_id = ?",
(space_id,),
).fetchone()
assert row is not None
connection.execute(
"""
INSERT OR IGNORE INTO space_permission_profile_revisions(
revision_id, space_id, profile_name, sandbox_mode,
approval_mode, reviewer_mode, revision, created_by,
created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
f"space:{space_id}:{next_revision}",
space_id,
profile_name,
sandbox_mode,
approval_mode,
reviewer_mode,
next_revision,
updated_by,
timestamp,
),
)
if audit_request_id:
audit_event_id = (
f"permission-profile:{space_id}:{audit_request_id}"
)
details_json = canonical_json(
{
"profile_name": profile_name,
"revision": next_revision,
}
)
existing_audit = connection.execute(
"""
SELECT * FROM security_audit_events
WHERE audit_event_id = ?
""",
(audit_event_id,),
).fetchone()
audit_values = (
space_id,
"permission.profile.modified",
"user",
updated_by,
details_json,
)
if existing_audit is not None:
persisted = (
existing_audit["space_id"],
existing_audit["event_type"],
existing_audit["actor_type"],
existing_audit["actor_id"],
existing_audit["details_json"],
)
if persisted != audit_values:
raise IdempotencyConflictError(
f"audit request {audit_request_id!r} was reused"
)
else:
connection.execute(
"""
INSERT INTO security_audit_events(
audit_event_id, space_id, run_id, interaction_id,
event_type, actor_type, actor_id, action_digest,
details_json, created_at
) VALUES (?, ?, NULL, NULL, ?, ?, ?, NULL, ?, ?)
""",
(audit_event_id, *audit_values, timestamp),
)
return self._space_permission_profile_from_row(row)
def create_approval_rule(
self,
*,
rule_id: str,
space_id: str,
effect: str,
action_pattern: str,
resource_pattern: str | None,
scope: str,
run_id: str | None,
source_interaction_id: str | None,
expires_at: float | None,
created_by: str,
now: float | None = None,
) -> ApprovalRuleRecord:
if effect not in {"allow", "prompt", "deny"}:
raise ValueError(
"approval rule effect must be allow, prompt, or deny"
)
if scope not in {"run", "space"}:
raise ValueError("approval rule scope must be run or space")
if scope == "run" and not run_id:
raise ValueError("run-scoped approval rules require run_id")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
existing = connection.execute(
"SELECT * FROM approval_rules WHERE rule_id = ?", (rule_id,)
).fetchone()
values = (
space_id,
effect,
action_pattern,
resource_pattern,
scope,
run_id,
source_interaction_id,
expires_at,
created_by,
)
if existing is not None:
persisted = tuple(
existing[key]
for key in (
"space_id",
"effect",
"action_pattern",
"resource_pattern",
"scope",
"run_id",
"source_interaction_id",
"expires_at",
"created_by",
)
)
if persisted != values:
raise IdempotencyConflictError(
f"approval rule {rule_id!r} was reused"
)
# Replaying a persisted rule must not convert arbitrary SQLite
# contents into an in-process ALLOW attestation.
return self._approval_rule_from_row(existing)
connection.execute(
"""
INSERT INTO approval_rules(
rule_id, space_id, effect, action_pattern,
resource_pattern, scope, run_id,
source_interaction_id, expires_at, created_by, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(rule_id, *values, timestamp),
)
row = connection.execute(
"SELECT * FROM approval_rules WHERE rule_id = ?", (rule_id,)
).fetchone()
assert row is not None
resolved = self._approval_rule_from_row(row)
self._trusted_approval_rules.add(resolved.rule_id)
return resolved
def approval_rule_is_trusted(self, rule_id: str) -> bool:
return rule_id in self._trusted_approval_rules
def list_approval_rules(
self,
*,
space_id: str,
run_id: str | None = None,
now: float | None = None,
) -> list[ApprovalRuleRecord]:
timestamp = now if now is not None else time.time()
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM approval_rules
WHERE space_id = ?
AND (expires_at IS NULL OR expires_at > ?)
AND (scope = 'space' OR (scope = 'run' AND run_id = ?))
ORDER BY created_at, rule_id
""",
(space_id, timestamp, run_id),
).fetchall()
return [self._approval_rule_from_row(row) for row in rows]
def append_security_audit_event(
self,
*,
audit_event_id: str,
event_type: str,
actor_type: str,
details: dict[str, Any] | None = None,
space_id: str | None = None,
run_id: str | None = None,
interaction_id: str | None = None,
actor_id: str | None = None,
action_digest: str | None = None,
now: float | None = None,
) -> SecurityAuditEventRecord:
timestamp = now if now is not None else time.time()
details_json = canonical_json(details or {})
with self._write_transaction() as connection:
existing = connection.execute(
"SELECT * FROM security_audit_events WHERE audit_event_id = ?",
(audit_event_id,),
).fetchone()
values = (
space_id,
run_id,
interaction_id,
event_type,
actor_type,
actor_id,
action_digest,
details_json,
)
if existing is not None:
persisted = tuple(
existing[key]
for key in (
"space_id",
"run_id",
"interaction_id",
"event_type",
"actor_type",
"actor_id",
"action_digest",
"details_json",
)
)
if persisted != values:
raise IdempotencyConflictError(
f"audit_event_id {audit_event_id!r} was reused"
)
return self._security_audit_event_from_row(existing)
connection.execute(
"""
INSERT INTO security_audit_events(
audit_event_id, space_id, run_id, interaction_id,
event_type, actor_type, actor_id, action_digest,
details_json, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(audit_event_id, *values, timestamp),
)
row = connection.execute(
"SELECT * FROM security_audit_events WHERE audit_event_id = ?",
(audit_event_id,),
).fetchone()
assert row is not None
return self._security_audit_event_from_row(row)
@staticmethod
def _validate_waiting_attempt(
connection: sqlite3.Connection,
*,
run_id: str,
attempt_id: str | None,
active_attempt_id: str | None,
interaction_label: str,
) -> None:
if attempt_id is not None:
attempt = connection.execute(
"SELECT run_id, status FROM run_attempts WHERE attempt_id = ?",
(attempt_id,),
).fetchone()
if attempt is None or attempt["run_id"] != run_id:
raise IdempotencyConflictError(
f"attempt {attempt_id!r} does not belong to run {run_id!r}"
)
if (
not transition_allowed(
ATTEMPT_TRANSITIONS,
str(attempt["status"]),
"waiting_for_user",
)
or active_attempt_id != attempt_id
):
raise InvalidRunTransitionError(
f"{interaction_label} attempt {attempt_id!r} must be the "
"active running attempt"
)
elif active_attempt_id is not None:
raise InvalidRunTransitionError(
f"{interaction_label} for a Run with an active attempt must "
"bind that attempt"
)
@staticmethod
def _resolve_waiting_attempt(
connection: sqlite3.Connection,
*,
attempt_id: str | None,
continue_active_attempt: bool,
timestamp: float,
outcome: str,
) -> bool:
if attempt_id is None:
return False
attempt = connection.execute(
"SELECT status FROM run_attempts WHERE attempt_id = ?",
(attempt_id,),
).fetchone()
status = attempt["status"] if attempt is not None else None
if status in ATTEMPT_ACTIVE_STATES and status != "waiting_for_user":
raise InvalidRunTransitionError(
f"interaction attempt {attempt_id!r} is in active state "
f"{status!r}, not waiting_for_user"
)
can_continue = bool(
continue_active_attempt and status == "waiting_for_user"
)
if can_continue:
connection.execute(
"""
UPDATE run_attempts
SET status = 'running', last_consumer_heartbeat_at = ?
WHERE attempt_id = ? AND status = 'waiting_for_user'
""",
(timestamp, attempt_id),
)
else:
connection.execute(
"""
UPDATE run_attempts
SET status = 'interrupted', ended_at = COALESCE(ended_at, ?),
outcome = ?
WHERE attempt_id = ? AND status = 'waiting_for_user'
""",
(timestamp, outcome, attempt_id),
)
return can_continue
def create_approval(
self,
*,
approval_id: str,
run_id: str,
attempt_id: str | None,
prompt: dict[str, Any],
action_digest: str | None = None,
policy_revision: str = "legacy",
safety_class: str = "unknown",
decision_scope: str = "once",
expires_at: float | None = None,
expiry_action: str = "keep_pending",
now: float | None = None,
) -> ApprovalRecord:
if expiry_action not in {"keep_pending", "reject"}:
raise ValueError("invalid approval expiry action")
timestamp = now if now is not None else time.time()
prompt_json = json.dumps(
prompt, ensure_ascii=False, separators=(",", ":"), sort_keys=True
)
resolved_action_digest = action_digest or canonical_digest(
{
"kind": "legacy_approval",
"run_id": run_id,
"attempt_id": attempt_id,
"prompt": prompt,
}
)
with self._write_transaction() as connection:
existing = connection.execute(
"SELECT * FROM approvals WHERE approval_id = ?", (approval_id,)
).fetchone()
if existing is not None:
if (
existing["run_id"] != run_id
or existing["attempt_id"] != attempt_id
or existing["prompt_json"] != prompt_json
or existing["expires_at"] != expires_at
or existing["expiry_action"] != expiry_action
or existing["action_digest"] != resolved_action_digest
or existing["policy_revision"] != policy_revision
or existing["safety_class"] != safety_class
or existing["decision_scope"] != decision_scope
):
raise IdempotencyConflictError(
f"approval_id {approval_id!r} was reused"
)
return self._approval_from_row(existing)
run = connection.execute(
"SELECT status, active_attempt_id FROM runs WHERE run_id = ?",
(run_id,),
).fetchone()
if run is None:
raise RunNotFoundError(f"run_id {run_id!r} does not exist")
if run["status"] in {"completed", "failed", "cancelled"}:
raise InvalidRunTransitionError(
f"terminal run {run_id!r} cannot request approval"
)
self._validate_waiting_attempt(
connection,
run_id=run_id,
attempt_id=attempt_id,
active_attempt_id=run["active_attempt_id"],
interaction_label="approval",
)
connection.execute(
"""
INSERT INTO human_interactions(
interaction_id, run_id, attempt_id, interaction_type,
status, request_json, response_schema_json, requested_by,
version, expires_at, presented_at, resolved_at,
created_at, updated_at
) VALUES (?, ?, ?, 'approval', 'requested', ?, '{}',
'permission_policy', 0, ?, NULL, NULL, ?, ?)
""",
(
approval_id,
run_id,
attempt_id,
prompt_json,
expires_at,
timestamp,
timestamp,
),
)
connection.execute(
"""
INSERT INTO approvals(
approval_id, run_id, attempt_id, status, prompt_json,
decision_json, created_at, resolved_at, version,
expires_at, expiry_action, action_digest, policy_revision,
safety_class, decision_scope
) VALUES (?, ?, ?, 'pending', ?, NULL, ?, NULL, 0, ?, ?, ?, ?, ?, ?)
""",
(
approval_id,
run_id,
attempt_id,
prompt_json,
timestamp,
expires_at,
expiry_action,
resolved_action_digest,
policy_revision,
safety_class,
decision_scope,
),
)
if attempt_id is not None:
updated_attempt = connection.execute(
"""
UPDATE run_attempts SET status = 'waiting_for_user'
WHERE attempt_id = ? AND status = 'running'
""",
(attempt_id,),
)
if updated_attempt.rowcount != 1:
raise InvalidRunTransitionError(
f"approval attempt {attempt_id!r} is no longer running"
)
self._append_event_in_transaction(
connection,
run_id,
RunEventDraft(
event_id=f"approval:{approval_id}:requested",
event_type="approval.requested",
payload={
"approval_id": approval_id,
"version": 0,
"attempt_id": attempt_id,
"prompt": prompt,
"expires_at": expires_at,
"expiry_action": expiry_action,
"action_digest": resolved_action_digest,
"policy_revision": policy_revision,
"safety_class": safety_class,
"decision_scope": decision_scope,
},
created_at=timestamp,
),
run_status="waiting_for_user",
active_attempt_id=attempt_id,
)
row = connection.execute(
"SELECT * FROM approvals WHERE approval_id = ?", (approval_id,)
).fetchone()
assert row is not None
return self._approval_from_row(row)
def approval_decision_is_trusted(
self,
approval_id: str,
*,
version: int,
action_digest: str,
) -> bool:
"""Return whether this process committed the dispatching decision.
SQLite remains the recovery/audit source of truth, while this volatile
attestation prevents a sibling process from turning a direct DB edit
into authority for an already-waiting live tool call. Restart loses the
attestation and therefore fails closed instead of replaying approval.
"""
return (approval_id, version, action_digest) in (
self._trusted_approval_decisions
)
def decide_approval(
self,
approval_id: str,
*,
decision: str,
details: dict[str, Any] | None = None,
expected_version: int,
expected_run_id: str | None = None,
continue_active_attempt: bool = False,
decision_request_id: str | None = None,
action_digest: str | None = None,
actor_type: str = "user",
actor_id: str | None = None,
source: str = "desktop",
decision_scope: str = "once",
rule_space_id: str | None = None,
rule_id: str | None = None,
rule_action_pattern: str | None = None,
rule_resource_pattern: str | None = None,
rule_expires_at: float | None = None,
now: float | None = None,
) -> ApprovalRecord:
if decision not in {"approved", "rejected"}:
raise ValueError("approval decision must be approved or rejected")
if actor_type not in {"user", "auto_reviewer", "system"}:
raise ValueError("invalid approval decision actor_type")
if source not in {"desktop", "remote_control", "recovery", "expiry"}:
raise ValueError("invalid approval decision source")
if decision_scope not in {"once", "run", "space"}:
raise ValueError(
"approval decision scope must be once, run, or space"
)
if decision == "approved" and decision_scope in {"run", "space"}:
if (
not rule_space_id
or not rule_id
or not rule_action_pattern
or not rule_resource_pattern
):
raise ValueError(
"bounded approval scope requires space, rule id, action, "
"and an exact resource matcher"
)
timestamp = now if now is not None else time.time()
decision_value = {"decision": decision, **(details or {})}
decision_json = json.dumps(
decision_value,
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
)
resolved_request_id = decision_request_id or (
f"legacy:{approval_id}:{expected_version + 1}"
)
with self._write_transaction() as connection:
approval = connection.execute(
"SELECT * FROM approvals WHERE approval_id = ?", (approval_id,)
).fetchone()
if approval is None:
raise RunNotFoundError(
f"approval_id {approval_id!r} does not exist"
)
if (
expected_run_id is not None
and approval["run_id"] != expected_run_id
):
raise IdempotencyConflictError(
f"approval {approval_id!r} does not belong to run {expected_run_id!r}"
)
if (
action_digest is not None
and approval["action_digest"] != action_digest
):
raise IdempotencyConflictError(
f"approval {approval_id!r} action digest changed"
)
duplicate = connection.execute(
"""
SELECT * FROM human_interaction_decisions
WHERE interaction_id = ? AND decision_request_id = ?
""",
(approval_id, resolved_request_id),
).fetchone()
if duplicate is not None:
if (
duplicate["decision_json"] != decision_json
or duplicate["actor_type"] != actor_type
or duplicate["actor_id"] != actor_id
or duplicate["source"] != source
or duplicate["action_digest"] != approval["action_digest"]
):
raise IdempotencyConflictError(
f"decision_request_id {resolved_request_id!r} was reused"
)
return self._approval_from_row(approval)
if approval["status"] != "pending":
if (
approval["status"] == decision
and approval["decision_json"] == decision_json
):
return self._approval_from_row(approval)
raise InvalidRunTransitionError(
f"approval {approval_id!r} is already resolved"
)
if int(approval["version"]) != expected_version:
raise OptimisticConcurrencyError(
f"approval {approval_id!r} expected version {expected_version}"
)
run = connection.execute(
"SELECT status FROM runs WHERE run_id = ?",
(approval["run_id"],),
).fetchone()
if run is None:
raise RunNotFoundError(
f"run_id {approval['run_id']!r} does not exist"
)
if run["status"] in {"completed", "failed", "cancelled"}:
raise InvalidRunTransitionError(
f"terminal run {approval['run_id']!r} cannot accept an approval decision"
)
can_continue = False
attempt_status: str | None = None
if continue_active_attempt and approval["attempt_id"] is not None:
attempt = connection.execute(
"SELECT status FROM run_attempts WHERE attempt_id = ?",
(approval["attempt_id"],),
).fetchone()
can_continue = (
attempt is not None
and attempt["status"] == "waiting_for_user"
)
attempt_status = (
attempt["status"] if attempt is not None else None
)
elif approval["attempt_id"] is not None:
attempt = connection.execute(
"SELECT status FROM run_attempts WHERE attempt_id = ?",
(approval["attempt_id"],),
).fetchone()
attempt_status = (
attempt["status"] if attempt is not None else None
)
if (
attempt_status in ATTEMPT_ACTIVE_STATES
and attempt_status != "waiting_for_user"
):
raise InvalidRunTransitionError(
f"approval {approval_id!r} is bound to active attempt state "
f"{attempt_status!r}, not waiting_for_user"
)
connection.execute(
"""
UPDATE approvals
SET status = ?, decision_json = ?, resolved_at = ?,
decision_scope = ?, version = version + 1
WHERE approval_id = ? AND version = ? AND status = 'pending'
""",
(
decision,
decision_json,
timestamp,
decision_scope,
approval_id,
expected_version,
),
)
connection.execute(
"""
INSERT INTO human_interaction_decisions(
decision_id, interaction_id, decision_request_id,
decision_json, actor_type, actor_id, source,
action_digest, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
str(uuid.uuid4()),
approval_id,
resolved_request_id,
decision_json,
actor_type,
actor_id,
source,
approval["action_digest"],
timestamp,
),
)
updated_interaction = connection.execute(
"""
UPDATE human_interactions
SET status = 'resolved', resolved_at = ?, updated_at = ?,
version = version + 1
WHERE interaction_id = ? AND version = ?
AND status IN ('requested', 'presented')
""",
(
timestamp,
timestamp,
approval_id,
expected_version,
),
)
if updated_interaction.rowcount != 1:
raise OptimisticConcurrencyError(
f"approval interaction {approval_id!r} changed while resolving"
)
if decision == "approved" and decision_scope in {"run", "space"}:
connection.execute(
"""
INSERT INTO approval_rules(
rule_id, space_id, effect, action_pattern,
resource_pattern, scope, run_id,
source_interaction_id, expires_at, created_by,
created_at
) VALUES (?, ?, 'allow', ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(rule_id) DO NOTHING
""",
(
rule_id,
rule_space_id,
rule_action_pattern,
rule_resource_pattern,
decision_scope,
approval["run_id"]
if decision_scope == "run"
else None,
approval_id,
rule_expires_at,
actor_id or actor_type,
timestamp,
),
)
connection.execute(
"""
INSERT OR IGNORE INTO security_audit_events(
audit_event_id, space_id, run_id, interaction_id,
event_type, actor_type, actor_id, action_digest,
details_json, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
f"approval-decision:{approval_id}:{resolved_request_id}",
rule_space_id,
approval["run_id"],
approval_id,
f"approval.{decision}",
actor_type,
actor_id,
approval["action_digest"],
canonical_json(
{
"decision_scope": decision_scope,
"source": source,
"rule_id": rule_id,
}
),
timestamp,
),
)
if can_continue:
connection.execute(
"""
UPDATE run_attempts
SET status = 'running', last_consumer_heartbeat_at = ?
WHERE attempt_id = ? AND status = 'waiting_for_user'
""",
(timestamp, approval["attempt_id"]),
)
elif approval["attempt_id"] is not None:
connection.execute(
"""
UPDATE run_attempts
SET status = 'interrupted', ended_at = COALESCE(ended_at, ?),
outcome = 'approval_decision_persisted'
WHERE attempt_id = ? AND status = 'waiting_for_user'
""",
(timestamp, approval["attempt_id"]),
)
self._append_event_in_transaction(
connection,
approval["run_id"],
RunEventDraft(
event_id=f"approval:{approval_id}:decision:{expected_version + 1}",
event_type="approval.decided",
payload={
"approval_id": approval_id,
"interaction_id": approval_id,
"decision_request_id": resolved_request_id,
"action_digest": approval["action_digest"],
"decision_scope": approval["decision_scope"],
"resolved_decision_scope": decision_scope,
"actor_type": actor_type,
"actor_id": actor_id,
"source": source,
"continued_attempt": can_continue,
**decision_value,
},
created_at=timestamp,
),
run_status="running" if can_continue else "interrupted",
active_attempt_id=(
approval["attempt_id"] if can_continue else None
),
clear_active_attempt=not can_continue,
)
row = connection.execute(
"SELECT * FROM approvals WHERE approval_id = ?", (approval_id,)
).fetchone()
assert row is not None
resolved = self._approval_from_row(row)
self._trusted_approval_decisions.add(
(resolved.approval_id, resolved.version, resolved.action_digest)
)
if decision == "approved" and decision_scope in {"run", "space"}:
assert rule_id is not None
self._trusted_approval_rules.add(rule_id)
return resolved
def list_approvals(
self, run_id: str, *, pending_only: bool = False
) -> list[ApprovalRecord]:
query = "SELECT * FROM approvals WHERE run_id = ?"
if pending_only:
query += " AND status = 'pending'"
query += " ORDER BY created_at"
with self._lock:
rows = self._connection.execute(query, (run_id,)).fetchall()
return [self._approval_from_row(row) for row in rows]
def record_timeout_outcome(
self, outcome: TimeoutOutcome
) -> CommittedRunEvent:
if outcome.scope in {
TimeoutScope.TRANSPORT_IDLE,
TimeoutScope.REMOTE_COMMAND_TTL,
TimeoutScope.CLOUD_SYNC,
}:
raise ValueError(
f"{outcome.scope.value} is not owned by RunJournal"
)
with self._write_transaction() as connection:
run = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (outcome.run_id,)
).fetchone()
if run is None:
raise RunNotFoundError(
f"run_id {outcome.run_id!r} does not exist"
)
event_type = {
TimeoutScope.RUNTIME_LIVENESS: "runtime.interrupted",
TimeoutScope.ACTIVITY: "activity.timed_out",
TimeoutScope.TOOL: "tool.timed_out",
TimeoutScope.RUN_DEADLINE: "run.deadline_reached",
TimeoutScope.APPROVAL_EXPIRY: "approval.expired",
}[outcome.scope]
run_status: str | None = None
clear_attempt = False
if outcome.scope is TimeoutScope.RUNTIME_LIVENESS:
run_status = "interrupted"
clear_attempt = True
elif outcome.scope is TimeoutScope.RUN_DEADLINE:
if run["deadline_at"] is None or outcome.ended_at < float(
run["deadline_at"]
):
raise InvalidRunTransitionError(
"run deadline outcome does not match a reached persisted deadline"
)
run_status = "failed"
clear_attempt = True
elif outcome.scope is TimeoutScope.TOOL:
tool = connection.execute(
"SELECT * FROM tool_calls WHERE tool_call_id = ?",
(outcome.tool_call_id,),
).fetchone()
if tool is None:
raise RunNotFoundError(
f"tool_call_id {outcome.tool_call_id!r} does not exist"
)
if tool["run_id"] != outcome.run_id:
raise InvalidRunTransitionError(
f"tool call {outcome.tool_call_id!r} belongs to run "
f"{tool['run_id']!r}, not {outcome.run_id!r}"
)
if tool["status"] in {"completed", "failed"}:
raise InvalidRunTransitionError(
f"tool call {outcome.tool_call_id!r} is already {tool['status']}"
)
if tool["status"] not in {
"dispatched",
"timed_out",
"outcome_unknown",
}:
raise InvalidRunTransitionError(
f"tool call {outcome.tool_call_id!r} cannot time out from "
f"{tool['status']!r}"
)
replayable = not self._tool_call_requires_fail_closed(tool)
tool_status = "timed_out" if replayable else "outcome_unknown"
event_type = (
"tool.timed_out" if replayable else "tool.outcome_unknown"
)
connection.execute(
"""
UPDATE tool_calls
SET status = ?, outcome = ?, timeout_reason = ?, updated_at = ?
WHERE tool_call_id = ?
""",
(
tool_status,
"retry_allowed" if replayable else "outcome_unknown",
outcome.reason,
outcome.ended_at,
outcome.tool_call_id,
),
)
elif outcome.scope is TimeoutScope.APPROVAL_EXPIRY:
approval = connection.execute(
"SELECT * FROM approvals WHERE approval_id = ?",
(outcome.approval_id,),
).fetchone()
if approval is None:
raise RunNotFoundError(
f"approval_id {outcome.approval_id!r} does not exist"
)
approval_run_id = approval["run_id"]
if approval_run_id != outcome.run_id:
raise InvalidRunTransitionError(
f"approval {outcome.approval_id!r} belongs to run "
f"{approval_run_id!r}, not {outcome.run_id!r}"
)
if approval["status"] != "pending":
raise InvalidRunTransitionError(
f"approval {outcome.approval_id!r} is already resolved"
)
if approval["expiry_action"] == "reject":
event_type = "approval.expired_rejected"
decision_json = json.dumps(
{
"decision": "rejected",
"reason": "approval_expired",
},
separators=(",", ":"),
sort_keys=True,
)
connection.execute(
"""
UPDATE approvals
SET status = 'rejected', decision_json = ?,
resolved_at = ?, version = version + 1
WHERE approval_id = ? AND status = 'pending'
""",
(
decision_json,
outcome.ended_at,
outcome.approval_id,
),
)
connection.execute(
"""
UPDATE human_interactions
SET status = 'expired', resolved_at = ?, updated_at = ?,
version = version + 1
WHERE interaction_id = ?
AND status IN ('requested', 'presented')
""",
(
outcome.ended_at,
outcome.ended_at,
outcome.approval_id,
),
)
connection.execute(
"""
INSERT OR IGNORE INTO human_interaction_decisions(
decision_id, interaction_id, decision_request_id,
decision_json, actor_type, actor_id, source,
action_digest, created_at
) VALUES (?, ?, ?, ?, 'system', NULL, 'expiry', ?, ?)
""",
(
str(uuid.uuid4()),
outcome.approval_id,
f"expiry:{outcome.approval_id}",
decision_json,
approval["action_digest"],
outcome.ended_at,
),
)
run_status = "interrupted"
clear_attempt = True
else:
event_type = "approval.expiry_observed"
if clear_attempt:
connection.execute(
"""
UPDATE run_attempts
SET status = ?, ended_at = COALESCE(ended_at, ?),
timeout_reason = ?, outcome = ?
WHERE run_id = ? AND status IN ('pending', 'running', 'waiting_for_user')
""",
(
"timed_out"
if outcome.scope is TimeoutScope.RUN_DEADLINE
else "interrupted",
outcome.ended_at,
outcome.reason,
event_type,
outcome.run_id,
),
)
identity = {
TimeoutScope.RUNTIME_LIVENESS: outcome.attempt_id,
TimeoutScope.ACTIVITY: outcome.activity_id,
TimeoutScope.TOOL: outcome.tool_call_id,
TimeoutScope.RUN_DEADLINE: outcome.run_id,
TimeoutScope.APPROVAL_EXPIRY: outcome.approval_id,
}[outcome.scope]
return self._append_event_in_transaction(
connection,
outcome.run_id,
RunEventDraft(
event_id=(
f"timeout:{outcome.scope.value}:{outcome.run_id}:"
f"{identity or outcome.ended_at}:{outcome.policy_version}"
),
event_type=event_type,
payload=outcome.to_payload(),
created_at=outcome.ended_at,
),
run_status=run_status,
clear_active_attempt=clear_attempt,
)
def reconcile_startup(
self, *, now: float | None = None
) -> StartupReconciliationResult:
timestamp = now if now is not None else time.time()
interrupted_runs: list[str] = []
completed_cancels: list[str] = []
deadline_runs: list[str] = []
detached_attempts: list[str] = []
unknown_tools: list[str] = []
unknown_model_invocations: list[str] = []
with self._write_transaction() as connection:
# A provider call is already across its dispatch boundary. If the
# Brain exited before committing its response, the model outcome
# cannot be inferred from the interrupted Run. Close it
# explicitly so training/replay consumers never mistake a stale
# `dispatched` row for a response that can still arrive.
pending_model_invocations = connection.execute(
"""
SELECT * FROM model_invocations
WHERE status = 'dispatched'
ORDER BY started_at, invocation_id
"""
).fetchall()
for invocation in pending_model_invocations:
try:
with self._savepoint(
connection, "startup_model_invocation"
):
updated = connection.execute(
"""
UPDATE model_invocations
SET status = 'outcome_unknown',
error_code = 'brain_restart_after_dispatch',
error_message = ?, completed_at = ?
WHERE invocation_id = ? AND status = 'dispatched'
""",
(
"Brain restarted after model dispatch and "
"before the response was durably recorded",
timestamp,
invocation["invocation_id"],
),
)
if updated.rowcount != 1:
continue
payload = {
"invocation_id": invocation["invocation_id"],
"attempt_id": invocation["attempt_id"],
"agent_id": invocation["agent_id"],
"status": "outcome_unknown",
"request_digest": invocation["request_digest"],
"response_digest": None,
"finish_reason": None,
"usage": {
"prompt_tokens": None,
"completion_tokens": None,
"cache_read_tokens": None,
"cache_write_tokens": None,
},
"error_code": "brain_restart_after_dispatch",
}
self._insert_model_invocation_event(
connection,
invocation_id=invocation["invocation_id"],
event_type="outcome_unknown",
payload=payload,
created_at=timestamp,
)
self._append_event_in_transaction(
connection,
invocation["run_id"],
RunEventDraft(
event_id=(
"startup:model-invocation-outcome-unknown:"
f"{invocation['invocation_id']}"
),
event_type="model.invocation.outcome_unknown",
payload=payload,
created_at=timestamp,
),
)
except Exception:
logger.exception(
"Startup reconciliation skipped one model invocation",
extra={"invocation_id": invocation["invocation_id"]},
)
continue
unknown_model_invocations.append(invocation["invocation_id"])
# A prepared ToolCall has not crossed the dispatch boundary. It
# therefore has a known, side-effect-free outcome after a process
# restart and must not remain as a permanent Project-wide blocker.
# Resolve its paired approval in the same transaction before the
# owning Run is interrupted below.
prepared_tools = connection.execute(
"""
SELECT * FROM tool_calls
WHERE status = 'prepared' AND outcome IS NULL
ORDER BY created_at, tool_call_id
"""
).fetchall()
for tool in prepared_tools:
try:
with self._savepoint(connection, "startup_prepared_tool"):
updated = connection.execute(
"""
UPDATE tool_calls
SET status = 'failed',
outcome = 'failed_before_dispatch',
updated_at = ?
WHERE tool_call_id = ?
AND status = 'prepared' AND outcome IS NULL
""",
(timestamp, tool["tool_call_id"]),
)
if updated.rowcount != 1:
continue
approval_id = f"approval:{tool['tool_call_id']}"
approval = connection.execute(
"""SELECT * FROM approvals
WHERE approval_id = ? AND status = 'pending'""",
(approval_id,),
).fetchone()
if approval is not None:
decision_json = json.dumps(
{
"decision": "rejected",
"reason": "brain_restart_before_dispatch",
},
separators=(",", ":"),
sort_keys=True,
)
connection.execute(
"""
UPDATE approvals
SET status = 'rejected', decision_json = ?,
resolved_at = ?, version = version + 1
WHERE approval_id = ? AND status = 'pending'
""",
(decision_json, timestamp, approval_id),
)
connection.execute(
"""
UPDATE human_interactions
SET status = 'cancelled', resolved_at = ?,
updated_at = ?, version = version + 1
WHERE interaction_id = ?
AND status IN ('requested', 'presented')
""",
(timestamp, timestamp, approval_id),
)
connection.execute(
"""
INSERT OR IGNORE INTO human_interaction_decisions(
decision_id, interaction_id,
decision_request_id, decision_json,
actor_type, actor_id, source, action_digest,
created_at
) VALUES (?, ?, ?, ?, 'system', NULL,
'recovery', ?, ?)
""",
(
str(uuid.uuid4()),
approval_id,
f"recovery:{approval_id}:before-dispatch",
decision_json,
approval["action_digest"],
timestamp,
),
)
self._append_event_in_transaction(
connection,
tool["run_id"],
RunEventDraft(
event_id=(
"startup:tool-failed-before-dispatch:"
f"{tool['tool_call_id']}"
),
event_type="tool.failed",
payload={
"tool_call_id": tool["tool_call_id"],
"safety_class": tool["safety_class"],
"reason": "brain_restart_before_dispatch",
"outcome_known": True,
},
created_at=timestamp,
),
)
except Exception:
logger.exception(
"Startup reconciliation skipped one prepared ToolCall",
extra={"tool_call_id": tool["tool_call_id"]},
)
continue
runs = connection.execute(
"""
SELECT * FROM runs
WHERE status IN ('pending', 'running', 'waiting_for_user')
OR (status = 'interrupted' AND cancel_request_id IS NOT NULL)
ORDER BY created_at
"""
).fetchall()
for run in runs:
run_interrupted = False
run_cancelled = False
run_deadline = False
run_attempt_ids: list[str] = []
try:
with self._savepoint(connection, "startup_run"):
active_attempts = connection.execute(
"""
SELECT * FROM run_attempts
WHERE run_id = ?
AND status IN ('pending', 'running', 'waiting_for_user')
""",
(run["run_id"],),
).fetchall()
pending_interaction = connection.execute(
"""
SELECT 1
FROM human_interactions
WHERE run_id = ?
AND status IN ('requested', 'presented')
LIMIT 1
""",
(run["run_id"],),
).fetchone()
if run["cancel_request_id"] is not None:
target_status = "cancelled"
event_type = "run.cancelled"
run_cancelled = True
elif (
run["deadline_at"] is not None
and float(run["deadline_at"]) <= timestamp
):
target_status = "failed"
event_type = "run.deadline_reached"
run_deadline = True
elif (
run["status"] == "waiting_for_user"
and not active_attempts
and pending_interaction is not None
):
continue
else:
target_status = (
"waiting_for_user"
if run["status"] == "waiting_for_user"
and pending_interaction is not None
else "interrupted"
)
event_type = "runtime.interrupted"
run_interrupted = True
run_attempt_ids = [
attempt["attempt_id"]
for attempt in active_attempts
]
connection.execute(
"""
UPDATE run_attempts
SET status = ?, ended_at = COALESCE(
ended_at,
last_consumer_heartbeat_at,
started_at,
?
),
outcome = COALESCE(outcome, ?)
WHERE run_id = ?
AND status IN ('pending', 'running', 'waiting_for_user')
""",
(
"cancelled"
if target_status == "cancelled"
else (
"timed_out"
if event_type == "run.deadline_reached"
else "interrupted"
),
timestamp,
event_type,
run["run_id"],
),
)
self._append_event_in_transaction(
connection,
run["run_id"],
RunEventDraft(
event_id=(
f"startup:{event_type}:{run['run_id']}:"
f"{run['version']}"
),
event_type=event_type,
payload={
"previous_status": run["status"],
"reason": "brain_restart",
"policy_version": run[
"timeout_policy_version"
],
},
created_at=timestamp,
),
run_status=target_status,
clear_active_attempt=True,
)
except Exception:
logger.exception(
"Startup reconciliation skipped one Run",
extra={"run_id": run["run_id"]},
)
continue
detached_attempts.extend(run_attempt_ids)
if run_interrupted:
interrupted_runs.append(run["run_id"])
if run_cancelled:
completed_cancels.append(run["run_id"])
if run_deadline:
deadline_runs.append(run["run_id"])
tool_rows = connection.execute(
"""
SELECT * FROM tool_calls
WHERE status = 'dispatched' AND outcome IS NULL
"""
).fetchall()
for tool in tool_rows:
try:
with self._savepoint(connection, "startup_tool"):
replayable = not self._tool_call_requires_fail_closed(
tool
)
tool_status = (
"timed_out" if replayable else "outcome_unknown"
)
tool_outcome = (
"retry_allowed"
if replayable
else "outcome_unknown"
)
event_type = (
"tool.timed_out"
if replayable
else "tool.outcome_unknown"
)
connection.execute(
"""
UPDATE tool_calls
SET status = ?, outcome = ?, timeout_reason = ?,
updated_at = ?
WHERE tool_call_id = ? AND status = 'dispatched'
""",
(
tool_status,
tool_outcome,
"brain_restart_after_dispatch",
timestamp,
tool["tool_call_id"],
),
)
self._append_event_in_transaction(
connection,
tool["run_id"],
RunEventDraft(
event_id=(
f"startup:{event_type}:"
f"{tool['tool_call_id']}"
),
event_type=event_type,
payload={
"tool_call_id": tool["tool_call_id"],
"safety_class": tool["safety_class"],
"reason": "brain_restart_after_dispatch",
"outcome": tool_outcome,
},
created_at=timestamp,
),
)
except Exception:
logger.exception(
"Startup reconciliation skipped one ToolCall",
extra={"tool_call_id": tool["tool_call_id"]},
)
continue
if not replayable:
unknown_tools.append(tool["tool_call_id"])
expired_approvals = connection.execute(
"""
SELECT approvals.*, runs.status AS run_status
FROM approvals
JOIN runs ON runs.run_id = approvals.run_id
WHERE approvals.status = 'pending'
AND approvals.expiry_action = 'reject'
AND approvals.expires_at IS NOT NULL
AND approvals.expires_at <= ?
ORDER BY approvals.expires_at, approvals.approval_id
""",
(timestamp,),
).fetchall()
for approval in expired_approvals:
if approval["run_status"] in {
"completed",
"failed",
"cancelled",
}:
continue
try:
with self._savepoint(connection, "startup_approval"):
decision_json = json.dumps(
{
"decision": "rejected",
"reason": "approval_expired",
},
separators=(",", ":"),
sort_keys=True,
)
connection.execute(
"""
UPDATE approvals
SET status = 'rejected', decision_json = ?,
resolved_at = ?, version = version + 1
WHERE approval_id = ? AND status = 'pending'
""",
(
decision_json,
timestamp,
approval["approval_id"],
),
)
connection.execute(
"""
UPDATE human_interactions
SET status = 'expired', resolved_at = ?,
updated_at = ?, version = version + 1
WHERE interaction_id = ?
AND status IN ('requested', 'presented')
""",
(
timestamp,
timestamp,
approval["approval_id"],
),
)
connection.execute(
"""
INSERT OR IGNORE INTO human_interaction_decisions(
decision_id, interaction_id,
decision_request_id, decision_json,
actor_type, actor_id, source, action_digest,
created_at
) VALUES (?, ?, ?, ?, 'system', NULL, 'expiry', ?, ?)
""",
(
str(uuid.uuid4()),
approval["approval_id"],
f"expiry:{approval['approval_id']}",
decision_json,
approval["action_digest"],
timestamp,
),
)
connection.execute(
"""
UPDATE run_attempts
SET status = 'interrupted',
ended_at = COALESCE(ended_at, ?),
outcome = 'approval_expired'
WHERE attempt_id = ?
AND status = 'waiting_for_user'
""",
(timestamp, approval["attempt_id"]),
)
self._append_event_in_transaction(
connection,
approval["run_id"],
RunEventDraft(
event_id=(
f"approval:{approval['approval_id']}:expired"
),
event_type="approval.expired_rejected",
payload={
"approval_id": approval["approval_id"],
"expiry_action": "reject",
"reason": "approval_expired",
},
created_at=timestamp,
),
run_status="interrupted",
clear_active_attempt=True,
)
except Exception:
logger.exception(
"Startup reconciliation skipped one Approval",
extra={"approval_id": approval["approval_id"]},
)
expired_interactions = connection.execute(
"""
SELECT human_interactions.*, runs.status AS run_status
FROM human_interactions
JOIN runs ON runs.run_id = human_interactions.run_id
WHERE human_interactions.interaction_type != 'approval'
AND human_interactions.status IN ('requested', 'presented')
AND human_interactions.expires_at IS NOT NULL
AND human_interactions.expires_at <= ?
ORDER BY human_interactions.expires_at,
human_interactions.interaction_id
""",
(timestamp,),
).fetchall()
for interaction in expired_interactions:
if interaction["run_status"] in {
"completed",
"failed",
"cancelled",
}:
continue
try:
with self._savepoint(connection, "startup_interaction"):
connection.execute(
"""
UPDATE human_interactions
SET status = 'expired', resolved_at = ?,
updated_at = ?, version = version + 1
WHERE interaction_id = ?
AND status IN ('requested', 'presented')
""",
(
timestamp,
timestamp,
interaction["interaction_id"],
),
)
connection.execute(
"""
UPDATE run_attempts
SET status = 'interrupted',
ended_at = COALESCE(ended_at, ?),
outcome = 'human_interaction_expired'
WHERE attempt_id = ?
AND status = 'waiting_for_user'
""",
(timestamp, interaction["attempt_id"]),
)
self._append_event_in_transaction(
connection,
interaction["run_id"],
RunEventDraft(
event_id=(
"interaction:"
f"{interaction['interaction_id']}:expired"
),
event_type="interaction.expired",
payload={
"interaction_id": interaction[
"interaction_id"
],
"interaction_type": interaction[
"interaction_type"
],
},
created_at=timestamp,
),
run_status="interrupted",
clear_active_attempt=True,
)
except Exception:
logger.exception(
"Startup reconciliation skipped one HumanInteraction",
extra={
"interaction_id": interaction["interaction_id"]
},
)
approvals = connection.execute(
"SELECT approval_id FROM approvals WHERE status = 'pending' ORDER BY created_at"
).fetchall()
commands = connection.execute(
"""
SELECT command_id FROM remote_command_inbox
WHERE state IN ('received', 'dispatched', 'accepted')
ORDER BY updated_at
"""
).fetchall()
bundle_installs = connection.execute(
"""
SELECT proposal_id FROM workspace_bundle_install_proposals
WHERE state = 'materializing'
ORDER BY updated_at, proposal_id
"""
).fetchall()
connection.execute(
"""
UPDATE workspace_bundle_install_proposals
SET state = 'needs_attention', version = version + 1,
error_code = 'desktop_restarted_during_materialization',
updated_at = ?
WHERE state = 'materializing'
""",
(timestamp,),
)
return StartupReconciliationResult(
interrupted_run_ids=tuple(interrupted_runs),
completed_cancel_run_ids=tuple(completed_cancels),
deadline_run_ids=tuple(deadline_runs),
detached_attempt_ids=tuple(detached_attempts),
outcome_unknown_tool_call_ids=tuple(unknown_tools),
pending_approval_ids=tuple(
row["approval_id"] for row in approvals
),
reconcilable_command_ids=tuple(
row["command_id"] for row in commands
),
reconcilable_bundle_install_ids=tuple(
row["proposal_id"] for row in bundle_installs
),
outcome_unknown_model_invocation_ids=tuple(
unknown_model_invocations
),
)
def persist_remote_command(
self,
*,
command_id: str,
session_id: str,
user_id: int,
project_id: str,
run_id: str | None,
route_version: int,
command_type: str,
payload: dict[str, Any],
expires_at: float,
receipt_grace_until: float,
requires_online_receipt_confirmation: bool,
delivery_lease_token: str | None = None,
receipt_event_id: str | None = None,
now: float | None = None,
) -> RemoteCommandInboxRecord:
"""Commit the Inbox row and receipt event/outbox in one transaction."""
if route_version < 1:
raise ValueError("route_version must be positive")
if receipt_grace_until < expires_at:
raise ValueError("receipt grace must not precede expiry")
timestamp = now if now is not None else time.time()
receipt_id = receipt_event_id or str(uuid.uuid4())
payload_json = json.dumps(
payload,
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
)
with self._write_transaction() as connection:
existing = connection.execute(
"SELECT * FROM remote_command_inbox WHERE command_id = ?",
(command_id,),
).fetchone()
if existing is not None:
canonical = (
existing["session_id"] == session_id
and int(existing["user_id"]) == user_id
and existing["project_id"] == project_id
and existing["run_id"] == run_id
and int(existing["route_version"]) == route_version
and existing["command_type"] == command_type
and existing["payload_json"] == payload_json
and float(existing["expires_at"]) == expires_at
and float(existing["receipt_grace_until"])
== receipt_grace_until
and bool(existing["requires_online_receipt_confirmation"])
== requires_online_receipt_confirmation
)
if not canonical:
raise IdempotencyConflictError(
f"command_id {command_id!r} was reused with different data"
)
if (
delivery_lease_token
and existing["receipt_status"] == "pending"
and existing["delivery_lease_token"]
!= delivery_lease_token
):
connection.execute(
"""
UPDATE remote_command_inbox
SET delivery_lease_token = ?, updated_at = ?
WHERE command_id = ? AND receipt_status = 'pending'
""",
(delivery_lease_token, timestamp, command_id),
)
existing = connection.execute(
"SELECT * FROM remote_command_inbox WHERE command_id = ?",
(command_id,),
).fetchone()
return self._command_from_row(existing)
connection.execute(
"""
INSERT INTO remote_command_inbox(
command_id, session_id, user_id, project_id, run_id,
route_version, command_type, payload_json, expires_at,
receipt_grace_until,
requires_online_receipt_confirmation, receipt_event_id,
delivery_lease_token,
receipt_status, state, dispatch_attempt_count, last_error,
created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
'pending', 'received', 0, NULL, ?, ?)
""",
(
command_id,
session_id,
user_id,
project_id,
run_id,
route_version,
command_type,
payload_json,
expires_at,
receipt_grace_until,
int(requires_online_receipt_confirmation),
receipt_id,
delivery_lease_token,
timestamp,
timestamp,
),
)
receipt_payload = "{}"
connection.execute(
"""
INSERT INTO command_result_events(
event_id, command_id, command_event_sequence, event_type,
payload_json, occurred_at
) VALUES (?, ?, 1, 'receipt.durably_received', ?, ?)
""",
(receipt_id, command_id, receipt_payload, timestamp),
)
connection.execute(
"""
INSERT INTO command_result_outbox(
event_id, command_id, command_event_sequence, status,
attempt_count, next_attempt_at, lease_token, lease_until,
last_error, created_at, updated_at
) VALUES (?, ?, 1, 'pending', 0, ?, NULL, NULL, NULL, ?, ?)
""",
(receipt_id, command_id, timestamp, timestamp, timestamp),
)
row = connection.execute(
"SELECT * FROM remote_command_inbox WHERE command_id = ?",
(command_id,),
).fetchone()
assert row is not None
return self._command_from_row(row)
def get_remote_command(
self, command_id: str
) -> RemoteCommandInboxRecord | None:
with self._lock:
row = self._connection.execute(
"SELECT * FROM remote_command_inbox WHERE command_id = ?",
(command_id,),
).fetchone()
return self._command_from_row(row) if row is not None else None
def get_latest_command_execution_result(
self, command_id: str
) -> CommandResultEvent | None:
"""Return the durable terminal execution result used for ACK replay."""
with self._lock:
row = self._connection.execute(
"""
SELECT * FROM command_result_events
WHERE command_id = ?
AND event_type IN ('execution.completed', 'execution.failed')
ORDER BY command_event_sequence DESC
LIMIT 1
""",
(command_id,),
).fetchone()
return (
self._command_event_from_row(row) if row is not None else None
)
def list_reconcilable_commands(
self, *, limit: int = 100
) -> list[RemoteCommandInboxRecord]:
with self._lock:
rows = self._connection.execute(
"""
SELECT * FROM remote_command_inbox
WHERE state IN ('received', 'dispatched', 'accepted')
ORDER BY updated_at, command_id
LIMIT ?
""",
(limit,),
).fetchall()
return [self._command_from_row(row) for row in rows]
def mark_command_dispatched(
self, command_id: str, *, now: float | None = None
) -> RemoteCommandInboxRecord:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
updated = connection.execute(
"""
UPDATE remote_command_inbox
SET state = 'dispatched',
dispatch_attempt_count = dispatch_attempt_count + 1,
updated_at = ?
WHERE command_id = ? AND state IN ('received', 'dispatched')
""",
(timestamp, command_id),
)
if updated.rowcount != 1:
row = connection.execute(
"SELECT * FROM remote_command_inbox WHERE command_id = ?",
(command_id,),
).fetchone()
if row is None:
raise RunNotFoundError(
f"command_id {command_id!r} does not exist"
)
return self._command_from_row(row)
row = connection.execute(
"SELECT * FROM remote_command_inbox WHERE command_id = ?",
(command_id,),
).fetchone()
assert row is not None
return self._command_from_row(row)
def set_command_receipt_status(
self,
command_id: str,
status: str,
*,
error: str | None = None,
now: float | None = None,
) -> RemoteCommandInboxRecord:
if status not in {"confirmed", "expired_late"}:
raise ValueError("invalid command receipt status")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
updated = connection.execute(
"""
UPDATE remote_command_inbox
SET receipt_status = ?, last_error = ?, updated_at = ?
WHERE command_id = ?
""",
(status, error, timestamp, command_id),
)
if updated.rowcount != 1:
raise RunNotFoundError(
f"command_id {command_id!r} does not exist"
)
row = connection.execute(
"SELECT * FROM remote_command_inbox WHERE command_id = ?",
(command_id,),
).fetchone()
assert row is not None
return self._command_from_row(row)
def append_command_result(
self,
command_id: str,
*,
event_type: str,
payload: dict[str, Any] | None = None,
event_id: str | None = None,
occurred_at: float | None = None,
) -> CommandResultEvent:
state_by_event = {
"admission.accepted": "accepted",
"admission.rejected": "rejected",
"execution.completed": "completed",
"execution.failed": "failed",
}
if (
event_type not in state_by_event
and event_type != "execution.started"
):
raise ValueError("unsupported command result event type")
timestamp = occurred_at if occurred_at is not None else time.time()
result_event_id = event_id or str(uuid.uuid4())
payload_value = payload or {}
payload_json = json.dumps(
payload_value,
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
)
with self._write_transaction() as connection:
duplicate = connection.execute(
"SELECT * FROM command_result_events WHERE event_id = ?",
(result_event_id,),
).fetchone()
if duplicate is not None:
if (
duplicate["command_id"] != command_id
or duplicate["event_type"] != event_type
or duplicate["payload_json"] != payload_json
):
raise IdempotencyConflictError(
f"command event {result_event_id!r} was reused"
)
return self._command_event_from_row(duplicate)
inbox = connection.execute(
"SELECT * FROM remote_command_inbox WHERE command_id = ?",
(command_id,),
).fetchone()
if inbox is None:
raise RunNotFoundError(
f"command_id {command_id!r} does not exist"
)
next_state = state_by_event.get(event_type)
if next_state is not None and not transition_allowed(
COMMAND_TRANSITIONS,
str(inbox["state"]),
next_state,
):
raise InvalidRunTransitionError(
f"command {command_id!r} cannot move from "
f"{inbox['state']!r} to {next_state!r}"
)
if (
event_type == "execution.started"
and inbox["state"] != "accepted"
):
raise InvalidRunTransitionError(
f"command {command_id!r} cannot start execution from "
f"{inbox['state']!r}"
)
sequence = int(
connection.execute(
"""
SELECT COALESCE(MAX(command_event_sequence), 0) + 1
FROM command_result_events WHERE command_id = ?
""",
(command_id,),
).fetchone()[0]
)
connection.execute(
"""
INSERT INTO command_result_events(
event_id, command_id, command_event_sequence, event_type,
payload_json, occurred_at
) VALUES (?, ?, ?, ?, ?, ?)
""",
(
result_event_id,
command_id,
sequence,
event_type,
payload_json,
timestamp,
),
)
connection.execute(
"""
INSERT INTO command_result_outbox(
event_id, command_id, command_event_sequence, status,
attempt_count, next_attempt_at, lease_token, lease_until,
last_error, created_at, updated_at
) VALUES (?, ?, ?, 'pending', 0, ?, NULL, NULL, NULL, ?, ?)
""",
(
result_event_id,
command_id,
sequence,
timestamp,
timestamp,
timestamp,
),
)
if next_state is not None:
connection.execute(
"""
UPDATE remote_command_inbox
SET state = ?, last_error = ?, updated_at = ?
WHERE command_id = ?
""",
(
next_state,
payload_value.get("error"),
timestamp,
command_id,
),
)
return CommandResultEvent(
event_id=result_event_id,
command_id=command_id,
command_event_sequence=sequence,
event_type=event_type,
payload=payload_value,
occurred_at=timestamp,
)
def claim_command_result_batches(
self,
*,
now: float | None = None,
max_commands: int = 8,
batch_size: int = 100,
lease_seconds: float = 30.0,
) -> list[CommandResultSyncBatch]:
if max_commands < 1 or batch_size < 1 or lease_seconds <= 0:
raise ValueError("command outbox claim limits must be positive")
timestamp = now if now is not None else time.time()
batches: list[CommandResultSyncBatch] = []
with self._write_transaction() as connection:
connection.execute(
"""
UPDATE command_result_outbox
SET status = 'pending', lease_token = NULL,
lease_until = NULL, updated_at = ?
WHERE status = 'sending'
AND (lease_until IS NULL OR lease_until <= ?)
""",
(timestamp, timestamp),
)
candidates = connection.execute(
"""
WITH heads AS (
SELECT command_id,
MIN(command_event_sequence) AS head_sequence
FROM command_result_outbox
WHERE status != 'sent'
GROUP BY command_id
)
SELECT heads.command_id, heads.head_sequence
FROM heads
JOIN command_result_outbox AS head
ON head.command_id = heads.command_id
AND head.command_event_sequence = heads.head_sequence
WHERE head.status = 'pending'
AND head.next_attempt_at <= ?
ORDER BY head.updated_at, heads.command_id
LIMIT ?
""",
(timestamp, max_commands),
).fetchall()
for candidate in candidates:
rows = connection.execute(
"""
SELECT e.*, o.status, o.attempt_count, o.next_attempt_at,
i.delivery_lease_token
FROM command_result_outbox AS o
JOIN command_result_events AS e ON e.event_id = o.event_id
JOIN remote_command_inbox AS i
ON i.command_id = o.command_id
WHERE o.command_id = ? AND o.command_event_sequence >= ?
AND o.status != 'sent'
ORDER BY o.command_event_sequence
LIMIT ?
""",
(
candidate["command_id"],
candidate["head_sequence"],
batch_size,
),
).fetchall()
ready: list[sqlite3.Row] = []
expected = int(candidate["head_sequence"])
for row in rows:
if (
int(row["command_event_sequence"]) != expected
or row["status"] != "pending"
or float(row["next_attempt_at"]) > timestamp
):
break
ready.append(row)
expected += 1
if not ready:
continue
token = uuid.uuid4().hex
event_ids = [row["event_id"] for row in ready]
updated = connection.execute(
"""
UPDATE command_result_outbox
SET status = 'sending', lease_token = ?, lease_until = ?,
updated_at = ?
WHERE status = 'pending' AND event_id IN (
SELECT value FROM json_each(?)
)
""",
(
token,
timestamp + lease_seconds,
timestamp,
json.dumps(event_ids, separators=(",", ":")),
),
)
if updated.rowcount != len(event_ids):
raise OutboxLeaseLostError(
f"failed to lease command lane {candidate['command_id']!r}"
)
batches.append(
CommandResultSyncBatch(
command_id=candidate["command_id"],
delivery_lease_token=ready[0]["delivery_lease_token"],
lease_token=token,
attempt_count=int(ready[0]["attempt_count"]),
events=tuple(
self._command_event_from_row(row) for row in ready
),
)
)
return batches
def mark_command_result_batch_sent(
self,
batch: CommandResultSyncBatch,
*,
now: float | None = None,
) -> None:
self._finish_command_result_batch(batch, "sent", now=now)
def retry_command_result_batch(
self,
batch: CommandResultSyncBatch,
*,
error: str,
next_attempt_at: float,
now: float | None = None,
) -> None:
self._finish_command_result_batch(
batch,
"pending",
error=error,
next_attempt_at=next_attempt_at,
increment_attempt=True,
now=now,
)
def block_command_result_batch(
self,
batch: CommandResultSyncBatch,
*,
failed_event_id: str,
error: str,
now: float | None = None,
) -> None:
timestamp = now if now is not None else time.time()
event_ids = [event.event_id for event in batch.events]
if failed_event_id not in event_ids:
raise ValueError("failed event must belong to command batch")
with self._write_transaction() as connection:
self._assert_command_batch_lease(connection, batch, event_ids)
connection.execute(
"""
UPDATE command_result_outbox
SET status = 'pending', lease_token = NULL,
lease_until = NULL, updated_at = ?
WHERE event_id IN (SELECT value FROM json_each(?))
""",
(timestamp, json.dumps(event_ids, separators=(",", ":"))),
)
connection.execute(
"""
UPDATE command_result_outbox
SET status = 'dead_letter', attempt_count = attempt_count + 1,
last_error = ?, updated_at = ?
WHERE event_id = ?
""",
(error[:4000], timestamp, failed_event_id),
)
def list_pending_outbox(
self, *, now: float | None = None, limit: int = 100
) -> list[RunEventSyncOutboxRecord]:
timestamp = now if now is not None else time.time()
with self._lock:
rows = self._connection.execute(
"""
SELECT *
FROM run_event_sync_outbox
WHERE status = 'pending' AND next_attempt_at <= ?
ORDER BY run_id, run_sequence
LIMIT ?
""",
(timestamp, limit),
).fetchall()
return [self._outbox_from_row(row) for row in rows]
def claim_ready_artifact_uploads(
self,
*,
now: float | None = None,
limit: int = 4,
lease_seconds: float = 60.0,
) -> list[ArtifactUploadSyncItem]:
if limit < 1 or lease_seconds <= 0:
raise ValueError("artifact upload claim limits must be positive")
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
connection.execute(
"""
UPDATE artifact_upload_outbox
SET status = 'pending', lease_token = NULL,
lease_until = NULL, updated_at = ?
WHERE status = 'sending'
AND (lease_until IS NULL OR lease_until <= ?)
""",
(timestamp, timestamp),
)
rows = connection.execute(
"""
SELECT artifact_upload_outbox.*
FROM artifact_upload_outbox
JOIN runs USING(run_id)
WHERE artifact_upload_outbox.status = 'pending'
AND artifact_upload_outbox.next_attempt_at <= ?
AND runs.status IN ('completed', 'failed', 'cancelled')
ORDER BY artifact_upload_outbox.created_at,
artifact_upload_outbox.artifact_id
LIMIT ?
""",
(timestamp, limit),
).fetchall()
claimed: list[ArtifactUploadSyncItem] = []
for row in rows:
lease_token = uuid.uuid4().hex
updated = connection.execute(
"""
UPDATE artifact_upload_outbox
SET status = 'sending', lease_token = ?, lease_until = ?,
updated_at = ?
WHERE artifact_id = ? AND status = 'pending'
""",
(
lease_token,
timestamp + lease_seconds,
timestamp,
row["artifact_id"],
),
)
if updated.rowcount != 1:
continue
claimed.append(
ArtifactUploadSyncItem(
artifact_id=str(row["artifact_id"]),
run_id=str(row["run_id"]),
project_id=str(row["project_id"]),
local_path=str(row["local_path"]),
filename=str(row["filename"]),
relative_path=str(row["relative_path"]),
file_size=int(row["file_size"]),
lease_token=lease_token,
attempt_count=int(row["attempt_count"]),
)
)
return claimed
@staticmethod
def _assert_artifact_upload_lease(
connection: sqlite3.Connection,
item: ArtifactUploadSyncItem,
) -> None:
row = connection.execute(
"""
SELECT status, lease_token FROM artifact_upload_outbox
WHERE artifact_id = ?
""",
(item.artifact_id,),
).fetchone()
if (
row is None
or row["status"] != "sending"
or row["lease_token"] != item.lease_token
):
raise OutboxLeaseLostError(
f"artifact upload lease lost for {item.artifact_id!r}"
)
def complete_artifact_upload(
self,
item: ArtifactUploadSyncItem,
*,
chat_file_id: int,
s3_bucket: str,
s3_key: str,
filename: str,
file_size: int,
file_type: str,
now: float | None = None,
) -> CommittedRunEvent:
timestamp = now if now is not None else time.time()
payload = {
"artifact_id": item.artifact_id,
"relativePath": item.relative_path,
"filename": filename,
"asset_ref": {
"chat_file_id": chat_file_id,
"bucket": s3_bucket,
"key": s3_key,
"filename": filename,
"size": file_size,
"content_type": file_type,
},
}
fingerprint = hashlib.sha256(
json.dumps(
{"run_id": item.run_id, **payload},
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
).hexdigest()
draft = RunEventDraft(
event_id=f"au_{fingerprint[:61]}",
event_type="artifact.uploaded",
payload=payload,
created_at=timestamp,
)
with self._write_transaction() as connection:
self._assert_artifact_upload_lease(connection, item)
event = self._append_event_in_transaction(
connection,
item.run_id,
draft,
expected_project_id=item.project_id,
)
connection.execute(
"""
UPDATE artifact_upload_outbox
SET status = 'sent', lease_token = NULL, lease_until = NULL,
last_error = NULL, updated_at = ?
WHERE artifact_id = ?
""",
(timestamp, item.artifact_id),
)
return event
def retry_artifact_upload(
self,
item: ArtifactUploadSyncItem,
*,
error: str,
next_attempt_at: float,
now: float | None = None,
dead_letter: bool = False,
) -> None:
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
self._assert_artifact_upload_lease(connection, item)
connection.execute(
"""
UPDATE artifact_upload_outbox
SET status = ?, attempt_count = attempt_count + 1,
next_attempt_at = ?, last_error = ?, lease_token = NULL,
lease_until = NULL, updated_at = ?
WHERE artifact_id = ?
""",
(
"dead_letter" if dead_letter else "pending",
next_attempt_at,
error[:4000],
timestamp,
item.artifact_id,
),
)
def claim_ready_outbox_batches(
self,
*,
now: float | None = None,
max_runs: int = 4,
batch_size: int = 100,
lease_seconds: float = 30.0,
) -> list[RunEventSyncBatch]:
"""Lease one consecutive FIFO batch from each ready Run.
A dead-letter row remains the earliest non-sent row and therefore blocks
only its own Run. Expired ``sending`` rows are reclaimed after a crash.
"""
if max_runs < 1 or batch_size < 1 or lease_seconds <= 0:
raise ValueError("outbox claim limits and lease must be positive")
timestamp = now if now is not None else time.time()
lease_until = timestamp + lease_seconds
batches: list[RunEventSyncBatch] = []
with self._write_transaction() as connection:
connection.execute(
"""
UPDATE run_event_sync_outbox
SET status = 'pending', lease_token = NULL,
lease_until = NULL, updated_at = ?
WHERE status = 'sending'
AND (lease_until IS NULL OR lease_until <= ?)
""",
(timestamp, timestamp),
)
candidates = connection.execute(
"""
SELECT o.run_id, r.project_id, o.run_sequence
FROM run_event_sync_outbox AS o
JOIN runs AS r ON r.run_id = o.run_id
WHERE o.status = 'pending'
AND o.next_attempt_at <= ?
AND o.run_sequence = (
SELECT MIN(head.run_sequence)
FROM run_event_sync_outbox AS head
WHERE head.run_id = o.run_id
AND head.status != 'sent'
)
ORDER BY o.updated_at, o.run_id
LIMIT ?
""",
(timestamp, max_runs),
).fetchall()
for candidate in candidates:
rows = connection.execute(
"""
SELECT e.event_id, e.run_id, e.sequence, e.run_version,
e.event_type, e.payload_json, e.legacy_step,
e.created_at, o.status, o.attempt_count,
o.next_attempt_at
FROM run_event_sync_outbox AS o
JOIN run_events AS e ON e.event_id = o.event_id
WHERE o.run_id = ? AND o.run_sequence >= ?
AND o.status != 'sent'
ORDER BY o.run_sequence
LIMIT ?
""",
(
candidate["run_id"],
candidate["run_sequence"],
batch_size,
),
).fetchall()
ready: list[sqlite3.Row] = []
expected = int(candidate["run_sequence"])
for row in rows:
if (
int(row["sequence"]) != expected
or row["status"] != "pending"
or float(row["next_attempt_at"]) > timestamp
):
break
ready.append(row)
expected += 1
if not ready:
continue
lease_token = uuid.uuid4().hex
event_ids = [row["event_id"] for row in ready]
claimed = connection.execute(
"""
UPDATE run_event_sync_outbox
SET status = 'sending', lease_token = ?, lease_until = ?,
updated_at = ?
WHERE status = 'pending' AND event_id IN (
SELECT value FROM json_each(?)
)
""",
(
lease_token,
lease_until,
timestamp,
json.dumps(event_ids, separators=(",", ":")),
),
)
if claimed.rowcount != len(event_ids):
raise OutboxLeaseLostError(
f"failed to lease all events for {candidate['run_id']!r}"
)
batches.append(
RunEventSyncBatch(
project_id=candidate["project_id"],
run_id=candidate["run_id"],
lease_token=lease_token,
attempt_count=int(ready[0]["attempt_count"]),
events=tuple(
self._event_from_row(row) for row in ready
),
)
)
return batches
def mark_outbox_batch_sent(
self,
batch: RunEventSyncBatch,
*,
now: float | None = None,
) -> None:
timestamp = now if now is not None else time.time()
event_ids = [event.event_id for event in batch.events]
with self._write_transaction() as connection:
self._assert_batch_lease(connection, batch, event_ids)
connection.execute(
"""
UPDATE run_event_sync_outbox
SET status = 'sent', lease_token = NULL, lease_until = NULL,
last_error = NULL, updated_at = ?
WHERE event_id IN (SELECT value FROM json_each(?))
""",
(timestamp, json.dumps(event_ids, separators=(",", ":"))),
)
def retry_outbox_batch(
self,
batch: RunEventSyncBatch,
*,
error: str,
next_attempt_at: float,
now: float | None = None,
) -> None:
timestamp = now if now is not None else time.time()
event_ids = [event.event_id for event in batch.events]
with self._write_transaction() as connection:
self._assert_batch_lease(connection, batch, event_ids)
connection.execute(
"""
UPDATE run_event_sync_outbox
SET status = 'pending', attempt_count = attempt_count + 1,
next_attempt_at = ?, last_error = ?, lease_token = NULL,
lease_until = NULL, updated_at = ?
WHERE event_id IN (SELECT value FROM json_each(?))
""",
(
next_attempt_at,
error[:4000],
timestamp,
json.dumps(event_ids, separators=(",", ":")),
),
)
def block_outbox_batch(
self,
batch: RunEventSyncBatch,
*,
failed_event_id: str,
error: str,
now: float | None = None,
) -> None:
timestamp = now if now is not None else time.time()
event_ids = [event.event_id for event in batch.events]
if failed_event_id not in event_ids:
raise ValueError("failed event must belong to the leased batch")
with self._write_transaction() as connection:
self._assert_batch_lease(connection, batch, event_ids)
connection.execute(
"""
UPDATE run_event_sync_outbox
SET status = 'pending', lease_token = NULL,
lease_until = NULL, updated_at = ?
WHERE event_id IN (SELECT value FROM json_each(?))
""",
(timestamp, json.dumps(event_ids, separators=(",", ":"))),
)
connection.execute(
"""
UPDATE run_event_sync_outbox
SET status = 'dead_letter', attempt_count = attempt_count + 1,
last_error = ?, updated_at = ?
WHERE event_id = ?
""",
(error[:4000], timestamp, failed_event_id),
)
@staticmethod
def _memory_entry_cloud_projection(
entry: dict[str, Any] | None,
sync_scope: str,
) -> dict[str, Any] | None:
if entry is None:
return None
if sync_scope != "full_memory":
raise InvalidRunTransitionError(
"Memory sync is fixed to full_memory"
)
projected = dict(entry)
from app.run_journal.memory_policy import (
normalize_memory_provenance_for_cloud,
)
projected["kind"], projected["source_trust"] = (
normalize_memory_provenance_for_cloud(
kind=str(projected.get("kind") or ""),
source_trust=str(projected.get("source_trust") or ""),
deleted=projected.get("deleted_at") is not None,
)
)
content = (
""
if projected.get("deleted_at") is not None
else str(projected.get("content") or "")
)
if content:
# Import lazily to keep the RunJournal storage module independent
# from permission-policy initialization while sharing the exact
# credential and device-identity projection used elsewhere.
from app.permission_policy.models import redact_sensitive_text
from app.workspace_config.models import redact_device_home_paths
content = redact_device_home_paths(redact_sensitive_text(content))
projected["content"] = content
projected["content_digest"] = hashlib.sha256(
content.encode("utf-8")
).hexdigest()
redacted_refs: list[str] = []
for value in projected.get("source_refs", [])[:20]:
text = str(value)
suffix = text.removeprefix("ref:")
if (
text.startswith("ref:")
and len(suffix) == 20
and all(
character in "0123456789abcdef" for character in suffix
)
):
redacted_refs.append(text)
else:
redacted_refs.append(
"ref:"
+ hashlib.sha256(text.encode("utf-8")).hexdigest()[:20]
)
projected["source_refs"] = redacted_refs
return projected
def list_memory_sync_snapshots(
self, account_owner_id: str
) -> list[dict[str, Any]]:
"""Return bounded derived snapshots for anti-entropy bootstrap."""
owner = account_owner_id.strip()
if not owner:
return []
with self._lock:
states = self._connection.execute(
"""
SELECT state.*
FROM memory_scope_state AS state
JOIN memory_scope_owners AS owner
ON owner.scope_type = state.scope_type
AND owner.scope_id = state.scope_id
WHERE state.sync_scope = 'full_memory'
AND owner.account_owner_id = ?
ORDER BY state.scope_type, state.scope_id
""",
(owner,),
).fetchall()
snapshots: list[dict[str, Any]] = []
for state in states:
entries = self._connection.execute(
"""
SELECT * FROM memory_entries
WHERE scope_type = ? AND scope_id = ?
ORDER BY created_at, memory_id
""",
(state["scope_type"], state["scope_id"]),
).fetchall()
sync_scope = str(state["sync_scope"])
snapshots.append(
{
"scope_type": state["scope_type"],
"scope_id": state["scope_id"],
"scope": {
"capture_enabled": bool(state["capture_enabled"]),
"use_enabled": bool(state["use_enabled"]),
"sync_scope": sync_scope,
"token_limit": int(state["token_limit"]),
"processed_through_watermark": state[
"processed_through_watermark"
],
"watermark_kind": state["watermark_kind"],
"updated_at": datetime.fromtimestamp(
float(state["updated_at"]), tz=UTC
).isoformat(),
},
"revision": int(state["revision"]),
"entries": [
self._memory_entry_cloud_projection(
{
"memory_id": row["memory_id"],
"kind": row["kind"],
"content": row["content"],
"priority": row["priority"],
"version": int(row["version"]),
"token_count": int(row["token_count"]),
"pinned_by_user": bool(
row["pinned_by_user"]
),
"confirmed_by_user": bool(
row["confirmed_by_user"]
),
"created_by": row["created_by"],
"source_trust": row["source_trust"],
"sensitivity": row["sensitivity"],
"source_refs": json.loads(
row["source_refs_json"]
),
"deleted_at": (
datetime.fromtimestamp(
float(row["deleted_at"]), tz=UTC
).isoformat()
if row["deleted_at"] is not None
else None
),
"created_at": datetime.fromtimestamp(
float(row["created_at"]), tz=UTC
).isoformat(),
"updated_at": datetime.fromtimestamp(
float(row["updated_at"]), tz=UTC
).isoformat(),
},
sync_scope,
)
for row in entries
],
}
)
return snapshots
def merge_cloud_memory_baseline(
self,
*,
scope_type: str,
scope_id: str,
account_owner_id: str,
scope: dict[str, Any],
entries: list[dict[str, Any]],
) -> int:
"""Hydrate a new writer before it publishes a rebase snapshot.
This is a read-model import, not a user/agent mutation: it never emits
a Memory mutation or advances the local revision. Local entries are
retained. Any differing entry with the same id becomes an explicit
review item; neither timestamp nor version is allowed to silently
overwrite user-visible Memory. The final union must still fit the
fixed scope cap.
"""
self._validate_memory_scope(scope_type, scope_id)
if scope.get("sync_scope") != "full_memory":
raise InvalidRunTransitionError(
"Cloud Memory baseline must contain full_memory"
)
def parse_timestamp(value: Any) -> float:
if isinstance(value, (int, float)):
return float(value)
if not isinstance(value, str):
raise ValueError("Cloud Memory timestamp is invalid")
return datetime.fromisoformat(
value.replace("Z", "+00:00")
).timestamp()
timestamp = time.time()
owner = account_owner_id.strip()
if not owner:
raise ValueError("Memory account owner is required")
with self._write_transaction() as connection:
ownership = connection.execute(
"""
SELECT account_owner_id FROM memory_scope_owners
WHERE scope_type = ? AND scope_id = ?
""",
(scope_type, scope_id),
).fetchone()
if ownership is None or ownership["account_owner_id"] != owner:
raise InvalidRunTransitionError(
"Cloud Memory baseline scope is not bound to the "
"authenticated account"
)
state = self._ensure_memory_scope_state_in_transaction(
connection,
scope_type=scope_type,
scope_id=scope_id,
owner_kind="desktop",
token_limit=_MEMORY_DEFAULT_TOKEN_LIMITS[scope_type],
now=timestamp,
)
seen: set[str] = set()
reconciliation_count = 0
for raw in entries:
memory_id = str(raw.get("memory_id") or "").strip()
if not memory_id or memory_id in seen:
raise IdempotencyConflictError(
"Cloud Memory baseline contains duplicate ids"
)
seen.add(memory_id)
content = str(raw.get("content") or "")
if hashlib.sha256(content.encode("utf-8")).hexdigest() != str(
raw.get("content_digest") or ""
):
raise IdempotencyConflictError(
"Cloud Memory baseline content digest does not match"
)
incoming_deleted = (
parse_timestamp(raw["deleted_at"])
if raw.get("deleted_at") is not None
else None
)
self._validate_memory_entry_values(
content=content,
kind=str(raw.get("kind") or ""),
priority=str(raw.get("priority") or "normal"),
token_count=int(raw.get("token_count") or 0),
created_by=str(raw.get("created_by") or ""),
source_trust=str(raw.get("source_trust") or ""),
sensitivity=str(raw.get("sensitivity") or "normal"),
allow_empty_content=incoming_deleted is not None,
)
incoming_kind = str(raw.get("kind") or "")
incoming_trust = str(raw.get("source_trust") or "")
incoming_created_by = str(raw.get("created_by") or "")
incoming_confirmed = bool(raw.get("confirmed_by_user"))
incoming_refs = list(raw.get("source_refs") or [])
assert_memory_entry_policy(
kind=incoming_kind,
content=content,
created_by=incoming_created_by,
source_trust=incoming_trust,
confirmed_by_user=incoming_confirmed,
source_refs=incoming_refs,
deleted=incoming_deleted is not None,
cloud_projection=True,
)
incoming_updated = parse_timestamp(raw.get("updated_at"))
incoming_created = parse_timestamp(raw.get("created_at"))
existing = connection.execute(
"SELECT * FROM memory_entries WHERE memory_id = ?",
(memory_id,),
).fetchone()
if existing is not None and (
existing["scope_type"] != scope_type
or existing["scope_id"] != scope_id
):
raise IdempotencyConflictError(
"Cloud Memory id belongs to another local scope"
)
incoming_version = int(raw.get("version") or 0)
if existing is not None:
local_deleted = (
float(existing["deleted_at"])
if existing["deleted_at"] is not None
else None
)
local_shape = {
"kind": existing["kind"],
"content": (
""
if local_deleted is not None
else existing["content"]
),
"priority": existing["priority"],
"token_count": int(existing["token_count"]),
"created_by": existing["created_by"],
"source_trust": existing["source_trust"],
"sensitivity": existing["sensitivity"],
"source_refs": json.loads(
existing["source_refs_json"]
),
"deleted_at": local_deleted,
}
cloud_shape = {
"kind": str(raw["kind"]),
"content": content,
"priority": str(raw.get("priority") or "normal"),
"token_count": int(raw["token_count"]),
"created_by": str(raw["created_by"]),
"source_trust": str(raw["source_trust"]),
"sensitivity": str(raw.get("sensitivity") or "normal"),
"source_refs": incoming_refs,
"deleted_at": incoming_deleted,
}
if local_shape == cloud_shape:
continue
reconciliation_id = (
"memrecon_"
+ hashlib.sha256(
json.dumps(
{
"owner": owner,
"scope_type": scope_type,
"scope_id": scope_id,
"memory_id": memory_id,
"local": local_shape,
"cloud": cloud_shape,
},
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
).hexdigest()[:32]
)
inserted = connection.execute(
"""
INSERT OR IGNORE INTO memory_reconciliation_items(
reconciliation_id, account_owner_id, scope_type,
scope_id, memory_id, local_entry_json,
cloud_entry_json, status, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?)
""",
(
reconciliation_id,
owner,
scope_type,
scope_id,
memory_id,
json.dumps(
local_shape,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
),
json.dumps(
dict(raw),
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
),
timestamp,
),
)
reconciliation_count += int(inserted.rowcount == 1)
continue
values = (
scope_type,
scope_id,
str(raw["kind"]),
content,
str(raw.get("priority") or "normal"),
incoming_version,
int(raw["token_count"]),
int(bool(raw.get("pinned_by_user"))),
int(bool(raw.get("confirmed_by_user"))),
str(raw["created_by"]),
str(raw["source_trust"]),
str(raw.get("sensitivity") or "normal"),
json.dumps(
incoming_refs,
ensure_ascii=False,
separators=(",", ":"),
),
incoming_deleted,
incoming_created,
incoming_updated,
memory_id,
)
if existing is None:
connection.execute(
"""
INSERT INTO memory_entries(
scope_type, scope_id, kind, content, priority,
version, token_count, pinned_by_user,
confirmed_by_user, created_by, source_trust,
sensitivity, source_refs_json, deleted_at,
created_at, updated_at, memory_id, usage_count
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, 0)
""",
values,
)
else:
connection.execute(
"""
UPDATE memory_entries
SET scope_type = ?, scope_id = ?, kind = ?, content = ?,
priority = ?, version = ?, token_count = ?,
pinned_by_user = ?, confirmed_by_user = ?,
created_by = ?, source_trust = ?, sensitivity = ?,
source_refs_json = ?, deleted_at = ?, created_at = ?,
updated_at = ?
WHERE memory_id = ?
""",
values,
)
active_tokens = int(
connection.execute(
"""
SELECT COALESCE(SUM(token_count), 0)
FROM memory_entries
WHERE scope_type = ? AND scope_id = ?
AND deleted_at IS NULL
""",
(scope_type, scope_id),
).fetchone()[0]
)
if active_tokens > int(state["token_limit"]):
raise InvalidRunTransitionError(
"Merged Cloud Memory baseline exceeds the local hard cap"
)
if int(state["revision"]) == 0:
connection.execute(
"""
UPDATE memory_scope_state
SET capture_enabled = ?, use_enabled = ?,
current_token_count = ?, updated_at = ?
WHERE scope_type = ? AND scope_id = ?
""",
(
int(bool(scope.get("capture_enabled"))),
int(bool(scope.get("use_enabled"))),
active_tokens,
timestamp,
scope_type,
scope_id,
),
)
else:
connection.execute(
"""
UPDATE memory_scope_state
SET current_token_count = ?, updated_at = ?
WHERE scope_type = ? AND scope_id = ?
""",
(active_tokens, timestamp, scope_type, scope_id),
)
return reconciliation_count
def get_memory_sync_status(
self, scope_type: str, scope_id: str
) -> dict[str, Any]:
"""Return truthful local delivery state for the Memory Center."""
with self._lock:
row = self._connection.execute(
"""
SELECT
SUM(CASE WHEN status IN ('pending', 'sending')
THEN 1 ELSE 0 END) AS pending_count,
SUM(CASE WHEN status = 'dead_letter'
THEN 1 ELSE 0 END) AS blocked_count,
MAX(CASE WHEN status != 'sent' THEN last_error END)
AS last_error,
MAX(CASE WHEN status = 'sent' THEN updated_at END)
AS last_synced_at
FROM memory_mutation_outbox
WHERE scope_type = ? AND scope_id = ?
""",
(scope_type, scope_id),
).fetchone()
pending = int(row["pending_count"] or 0)
blocked = int(row["blocked_count"] or 0)
return {
"state": (
"blocked" if blocked else "pending" if pending else "synced"
),
"pending_count": pending,
"blocked_count": blocked,
"last_error": row["last_error"],
"last_synced_at": row["last_synced_at"],
}
def has_legacy_memory_import(
self, source_path: str, source_checksum: str
) -> bool:
with self._lock:
row = self._connection.execute(
"""
SELECT 1 FROM legacy_memory_import_batches
WHERE source_path = ? AND source_checksum = ?
AND status = 'completed'
""",
(source_path, source_checksum),
).fetchone()
return row is not None
def record_legacy_memory_import(
self,
*,
source_path: str,
source_checksum: str,
status: str,
imported_count: int,
skipped_count: int,
error: str | None = None,
now: float | None = None,
) -> None:
if status not in {"completed", "degraded"}:
raise ValueError("invalid legacy Memory import status")
if imported_count < 0 or skipped_count < 0:
raise ValueError(
"legacy Memory import counts must be non-negative"
)
timestamp = now if now is not None else time.time()
with self._write_transaction() as connection:
connection.execute(
"""
INSERT INTO legacy_memory_import_batches(
source_path, source_checksum, status, imported_count,
skipped_count, last_error, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(source_path, source_checksum) DO UPDATE SET
status = excluded.status,
imported_count = excluded.imported_count,
skipped_count = excluded.skipped_count,
last_error = excluded.last_error,
updated_at = excluded.updated_at
""",
(
source_path,
source_checksum,
status,
imported_count,
skipped_count,
error[:4000] if error else None,
timestamp,
timestamp,
),
)
def claim_ready_memory_mutation_batches(
self,
*,
now: float | None = None,
max_scopes: int = 4,
batch_size: int = 100,
lease_seconds: float = 30.0,
eligible_scopes: set[tuple[str, str]] | None = None,
) -> list[MemoryMutationSyncBatch]:
if max_scopes < 1 or batch_size < 1 or lease_seconds <= 0:
raise ValueError("Memory outbox claim limits must be positive")
timestamp = now if now is not None else time.time()
batches: list[MemoryMutationSyncBatch] = []
with self._write_transaction() as connection:
connection.execute(
"""
UPDATE memory_mutation_outbox
SET status = 'pending', lease_token = NULL,
lease_until = NULL, updated_at = ?
WHERE status = 'sending'
AND (lease_until IS NULL OR lease_until <= ?)
""",
(timestamp, timestamp),
)
candidates = connection.execute(
"""
SELECT outbox.scope_type, outbox.scope_id,
MIN(outbox.scope_revision) AS head_scope_revision
FROM memory_mutation_outbox AS outbox
WHERE outbox.status = 'pending'
AND outbox.next_attempt_at <= ?
AND outbox.scope_revision = (
SELECT MIN(head.scope_revision)
FROM memory_mutation_outbox AS head
WHERE head.scope_type = outbox.scope_type
AND head.scope_id = outbox.scope_id
AND head.status != 'sent'
)
GROUP BY outbox.scope_type, outbox.scope_id
ORDER BY head_scope_revision, outbox.scope_type, outbox.scope_id
""",
(timestamp,),
).fetchall()
for candidate in candidates:
scope_key = (
str(candidate["scope_type"]),
str(candidate["scope_id"]),
)
if (
eligible_scopes is not None
and scope_key not in eligible_scopes
):
continue
if len(batches) >= max_scopes:
break
state = connection.execute(
"""
SELECT * FROM memory_scope_state
WHERE scope_type = ? AND scope_id = ?
""",
(candidate["scope_type"], candidate["scope_id"]),
).fetchone()
if state is None or state["sync_scope"] != "full_memory":
continue
rows = connection.execute(
"""
SELECT * FROM memory_mutation_outbox
WHERE scope_type = ? AND scope_id = ?
AND status != 'sent' AND scope_revision >= ?
ORDER BY scope_revision
LIMIT ?
""",
(
candidate["scope_type"],
candidate["scope_id"],
candidate["head_scope_revision"],
batch_size,
),
).fetchall()
ready: list[sqlite3.Row] = []
expected_scope_revision = int(rows[0]["scope_revision"])
for row in rows:
if (
row["status"] != "pending"
or float(row["next_attempt_at"]) > timestamp
or row["payload_json"] == "{}"
or int(row["scope_revision"])
!= expected_scope_revision
):
break
ready.append(row)
expected_scope_revision += 1
if not ready:
continue
lease_token = uuid.uuid4().hex
mutation_ids = [row["mutation_id"] for row in ready]
claimed = connection.execute(
"""
UPDATE memory_mutation_outbox
SET status = 'sending', lease_token = ?, lease_until = ?,
updated_at = ?
WHERE status = 'pending' AND mutation_id IN (
SELECT value FROM json_each(?)
)
""",
(
lease_token,
timestamp + lease_seconds,
timestamp,
json.dumps(mutation_ids, separators=(",", ":")),
),
)
if claimed.rowcount != len(ready):
raise OutboxLeaseLostError(
"failed to lease all Memory mutations"
)
sync_scope = str(state["sync_scope"])
items: list[MemoryMutationSyncItem] = []
for row in ready:
payload = json.loads(row["payload_json"])
payload["scope_revision"] = int(row["scope_revision"])
payload["entry"] = self._memory_entry_cloud_projection(
payload.get("entry"), sync_scope
)
items.append(
MemoryMutationSyncItem(
mutation_id=row["mutation_id"],
payload=payload,
)
)
batches.append(
MemoryMutationSyncBatch(
scope_type=candidate["scope_type"],
scope_id=candidate["scope_id"],
scope={
"capture_enabled": bool(state["capture_enabled"]),
"use_enabled": bool(state["use_enabled"]),
"sync_scope": sync_scope,
"token_limit": int(state["token_limit"]),
"processed_through_watermark": state[
"processed_through_watermark"
],
"watermark_kind": state["watermark_kind"],
"updated_at": datetime.fromtimestamp(
float(state["updated_at"]), tz=UTC
).isoformat(),
},
source_revision=int(state["revision"]),
lease_token=lease_token,
attempt_count=int(ready[0]["attempt_count"]),
items=tuple(items),
)
)
return batches
def _finish_memory_mutation_batch(
self,
batch: MemoryMutationSyncBatch,
status: str,
*,
error: str | None = None,
next_attempt_at: float | None = None,
increment_attempt: bool = False,
now: float | None = None,
) -> None:
timestamp = now if now is not None else time.time()
mutation_ids = [item.mutation_id for item in batch.items]
with self._write_transaction() as connection:
rows = connection.execute(
"""
SELECT mutation_id FROM memory_mutation_outbox
WHERE status = 'sending' AND lease_token = ?
AND mutation_id IN (SELECT value FROM json_each(?))
""",
(
batch.lease_token,
json.dumps(mutation_ids, separators=(",", ":")),
),
).fetchall()
if {row["mutation_id"] for row in rows} != set(mutation_ids):
raise OutboxLeaseLostError("Memory mutation lease was lost")
connection.execute(
"""
UPDATE memory_mutation_outbox
SET status = ?, attempt_count = attempt_count + ?,
next_attempt_at = COALESCE(?, next_attempt_at),
last_error = ?, lease_token = NULL, lease_until = NULL,
updated_at = ?
WHERE lease_token = ?
AND mutation_id IN (SELECT value FROM json_each(?))
""",
(
status,
int(increment_attempt),
next_attempt_at,
error[:4000] if error else None,
timestamp,
batch.lease_token,
json.dumps(mutation_ids, separators=(",", ":")),
),
)
def mark_memory_mutation_batch_sent(
self, batch: MemoryMutationSyncBatch, *, now: float | None = None
) -> None:
self._finish_memory_mutation_batch(batch, "sent", now=now)
def retry_memory_mutation_batch(
self,
batch: MemoryMutationSyncBatch,
*,
error: str,
next_attempt_at: float,
now: float | None = None,
) -> None:
self._finish_memory_mutation_batch(
batch,
"pending",
error=error,
next_attempt_at=next_attempt_at,
increment_attempt=True,
now=now,
)
def block_memory_mutation_batch(
self,
batch: MemoryMutationSyncBatch,
*,
failed_mutation_id: str,
error: str,
now: float | None = None,
) -> None:
if failed_mutation_id not in {
item.mutation_id for item in batch.items
}:
raise ValueError("failed mutation must belong to batch")
timestamp = now if now is not None else time.time()
mutation_ids = [item.mutation_id for item in batch.items]
with self._write_transaction() as connection:
rows = connection.execute(
"""
SELECT mutation_id FROM memory_mutation_outbox
WHERE status = 'sending' AND lease_token = ?
AND mutation_id IN (SELECT value FROM json_each(?))
""",
(
batch.lease_token,
json.dumps(mutation_ids, separators=(",", ":")),
),
).fetchall()
if {row["mutation_id"] for row in rows} != set(mutation_ids):
raise OutboxLeaseLostError("Memory mutation lease was lost")
connection.execute(
"""
UPDATE memory_mutation_outbox
SET status = 'pending', lease_token = NULL,
lease_until = NULL, updated_at = ?
WHERE lease_token = ?
AND mutation_id IN (SELECT value FROM json_each(?))
""",
(
timestamp,
batch.lease_token,
json.dumps(mutation_ids, separators=(",", ":")),
),
)
connection.execute(
"""
UPDATE memory_mutation_outbox
SET status = 'dead_letter', attempt_count = attempt_count + 1,
last_error = ?, updated_at = ?
WHERE mutation_id = ?
""",
(error[:4000], timestamp, failed_mutation_id),
)
@staticmethod
def _terminal_status_for_event(draft: RunEventDraft) -> str | None:
# assistant.final keeps legacy_step='end' for old projectors, but the
# Coordinator remains the sole owner of the Run terminal transition.
if draft.event_type == "assistant.final":
return None
if draft.event_type == "run.completed":
return "completed"
if draft.event_type in {"run.failed", "run.deadline_reached"}:
return "failed"
if draft.event_type == "run.cancelled":
return "cancelled"
if draft.event_type in {"run.interrupted", "runtime.interrupted"}:
return "interrupted"
return None
@staticmethod
def _bounded_frontier_text(value: Any, *, limit: int = 1000) -> str:
text = str(value or "").strip()
return text if len(text) <= limit else text[:limit] + ""
@classmethod
def _project_frontier_for_terminal_run(
cls,
connection: sqlite3.Connection,
*,
run_id: str,
terminal_status: str,
terminal_payload: dict[str, Any],
) -> dict[str, Any]:
rows = connection.execute(
"""
SELECT event_type, payload_json, legacy_step
FROM run_events
WHERE run_id = ?
AND (
event_type IN (
'user.message', 'tool.outcome_unknown',
'artifact.manifest.finalized'
)
OR legacy_step = 'todo_state'
)
ORDER BY sequence
""",
(run_id,),
).fetchall()
objective = ""
latest_todos: list[dict[str, Any]] = []
saw_todo_state = False
artifact_ids: set[str] = set()
outcome_unknown = False
for row in rows:
payload = json.loads(row["payload_json"])
if row["event_type"] == "user.message" and not objective:
objective = cls._bounded_frontier_text(
payload.get("content") or payload.get("message")
)
if row["legacy_step"] == "todo_state":
candidate = payload.get("todos")
if isinstance(candidate, list):
saw_todo_state = True
latest_todos = [
item for item in candidate if isinstance(item, dict)
][:100]
if row["event_type"] == "tool.outcome_unknown":
outcome_unknown = True
artifact_id = payload.get("artifact_id")
if isinstance(artifact_id, str) and artifact_id.strip():
if len(artifact_ids) < 500:
artifact_ids.add(artifact_id.strip())
many = payload.get("artifact_ids")
if isinstance(many, list):
for value in many:
if len(artifact_ids) >= 500:
break
if isinstance(value, str) and value.strip():
artifact_ids.add(value.strip())
artifacts = payload.get("artifacts")
if isinstance(artifacts, list):
for artifact in artifacts:
if isinstance(artifact, dict):
value = artifact.get("artifact_id") or artifact.get(
"id"
)
if isinstance(value, str) and value.strip():
if len(artifact_ids) < 500:
artifact_ids.add(value.strip())
completed: list[str] = []
remaining: list[str] = []
in_progress: str | None = None
next_action: str | None = None
for todo in latest_todos:
content = cls._bounded_frontier_text(todo.get("content"))
active_form = cls._bounded_frontier_text(todo.get("active_form"))
if not content:
continue
status = str(todo.get("status") or "pending")
if status == "completed":
completed.append(content)
elif status == "in_progress" and in_progress is None:
in_progress = active_form or content
next_action = in_progress
else:
remaining.append(content)
if next_action is None and remaining:
next_action = remaining[0]
owner = connection.execute(
"SELECT project_id FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
previous_state = (
connection.execute(
"SELECT frontier_json FROM project_execution_states WHERE project_id = ?",
(owner["project_id"],),
).fetchone()
if owner is not None
else None
)
previous_frontier = (
json.loads(previous_state["frontier_json"])
if previous_state is not None
and previous_state["frontier_json"] is not None
else None
)
continuation = connection.execute(
"SELECT * FROM continuation_claims WHERE request_id = ?",
(run_id,),
).fetchone()
if continuation is not None and isinstance(previous_frontier, dict):
objective = cls._bounded_frontier_text(
previous_frontier.get("objective")
)
for value in previous_frontier.get("artifact_ids", []):
if len(artifact_ids) >= 500:
break
if isinstance(value, str) and value:
artifact_ids.add(value)
if not saw_todo_state:
completed = [
cls._bounded_frontier_text(value)
for value in previous_frontier.get("completed", [])
if cls._bounded_frontier_text(value)
]
remaining = [
cls._bounded_frontier_text(value)
for value in previous_frontier.get("remaining", [])
if cls._bounded_frontier_text(value)
]
claimed_action = cls._bounded_frontier_text(
continuation["next_action"]
)
if terminal_status == "completed" and claimed_action:
if claimed_action not in completed:
completed.append(claimed_action)
remaining = [
value for value in remaining if value != claimed_action
]
in_progress = None
next_action = remaining[0] if remaining else None
elif terminal_status == "completed" and not latest_todos and objective:
completed = [objective]
blocked_by: str | None = None
if outcome_unknown:
blocked_by = "external_tool_outcome_unknown"
elif terminal_status != "completed":
blocked_by = cls._bounded_frontier_text(
terminal_payload.get("reason")
or terminal_payload.get("error")
or terminal_status
)
return {
"objective": objective,
"completed": completed,
"in_progress": in_progress,
"remaining": remaining,
"next_action": next_action,
"blocked_by": blocked_by,
"artifact_ids": sorted(artifact_ids),
}
@classmethod
def _refresh_project_execution_state_in_transaction(
cls,
connection: sqlite3.Connection,
*,
run_id: str,
terminal_status: str,
terminal_payload: dict[str, Any],
updated_at: float,
) -> None:
run = connection.execute(
"SELECT project_id FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
if run is None:
raise RunNotFoundError(f"run_id {run_id!r} does not exist")
frontier = cls._project_frontier_for_terminal_run(
connection,
run_id=run_id,
terminal_status=terminal_status,
terminal_payload=terminal_payload,
)
frontier_json = canonical_json(frontier)
frontier_digest = hashlib.sha256(
frontier_json.encode("utf-8")
).hexdigest()
current = connection.execute(
"SELECT * FROM project_execution_states WHERE project_id = ?",
(run["project_id"],),
).fetchone()
if (
current is not None
and current["frontier_digest"] == frontier_digest
):
return
next_version = int(current["state_version"]) + 1 if current else 1
connection.execute(
"""
INSERT INTO project_execution_states(
project_id, state_version, frontier_json, frontier_digest,
frontier_run_id, updated_at
) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(project_id) DO UPDATE SET
state_version = excluded.state_version,
frontier_json = excluded.frontier_json,
frontier_digest = excluded.frontier_digest,
frontier_run_id = excluded.frontier_run_id,
updated_at = excluded.updated_at
""",
(
run["project_id"],
next_version,
frontier_json,
frontier_digest,
run_id,
updated_at,
),
)
@classmethod
def _backfill_project_execution_state_in_transaction(
cls,
connection: sqlite3.Connection,
*,
project_id: str,
) -> None:
existing = connection.execute(
"SELECT 1 FROM project_execution_states WHERE project_id = ?",
(project_id,),
).fetchone()
if existing is not None:
return
run = connection.execute(
"""
SELECT run_id, status, updated_at
FROM runs
WHERE project_id = ?
AND status IN ('completed', 'failed', 'cancelled', 'interrupted')
ORDER BY updated_at DESC, created_at DESC, run_id DESC
LIMIT 1
""",
(project_id,),
).fetchone()
if run is None:
return
terminal_payload: dict[str, Any] = {}
events = connection.execute(
"""
SELECT event_type, payload_json, legacy_step
FROM run_events WHERE run_id = ? ORDER BY sequence DESC
""",
(run["run_id"],),
).fetchall()
for event in events:
candidate = RunEventDraft(
event_id="historical-frontier-probe",
event_type=event["event_type"],
payload=json.loads(event["payload_json"]),
legacy_step=event["legacy_step"],
)
if cls._terminal_status_for_event(candidate) == run["status"]:
terminal_payload = dict(candidate.payload)
break
cls._refresh_project_execution_state_in_transaction(
connection,
run_id=run["run_id"],
terminal_status=run["status"],
terminal_payload=terminal_payload,
updated_at=float(run["updated_at"]),
)
def _append_event_in_transaction(
self,
connection: sqlite3.Connection,
run_id: str,
draft: RunEventDraft,
*,
payload_json: str | None = None,
expected_version: int | None = None,
expected_project_id: str | None = None,
run_status: str | None = None,
active_attempt_id: str | None = None,
clear_active_attempt: bool = False,
allow_assistant_final: bool = False,
) -> CommittedRunEvent:
encoded_payload = payload_json or json.dumps(
dict(draft.payload),
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
)
duplicate = connection.execute(
"SELECT * FROM run_events WHERE event_id = ?",
(draft.event_id,),
).fetchone()
if duplicate is not None:
if expected_project_id is not None:
owner = connection.execute(
"SELECT project_id FROM runs WHERE run_id = ?",
(duplicate["run_id"],),
).fetchone()
if (
owner is not None
and owner["project_id"] != expected_project_id
):
raise IdempotencyConflictError(
f"event_id {draft.event_id!r} belongs to project "
f"{owner['project_id']!r}, not {expected_project_id!r}"
)
return self._resolve_duplicate_event(
connection,
duplicate,
run_id=run_id,
draft=draft,
payload_json=encoded_payload,
)
run = connection.execute(
"SELECT * FROM runs WHERE run_id = ?", (run_id,)
).fetchone()
if run is None:
raise RunNotFoundError(f"run_id {run_id!r} does not exist")
if run["origin"] == "cloud_restore":
raise InvalidRunTransitionError(
f"run {run_id!r} is read-only Cloud-restored history"
)
if draft.event_type == "assistant.final":
if not allow_assistant_final:
raise InvalidRunTransitionError(
"assistant.final may only be committed atomically with "
"run.completed"
)
if run["status"] not in {
"pending",
"running",
"waiting_for_user",
"interrupted",
}:
raise InvalidRunTransitionError(
f"terminal run {run_id!r} cannot accept assistant.final"
)
if run_status is not None and not transition_allowed(
RUN_TRANSITIONS,
str(run["status"]),
run_status,
):
raise InvalidRunTransitionError(
f"run {run_id!r} cannot move from {run['status']!r} to {run_status!r}"
)
if (
expected_project_id is not None
and run["project_id"] != expected_project_id
):
raise IdempotencyConflictError(
f"run_id {run_id!r} belongs to project {run['project_id']!r}, "
f"not {expected_project_id!r}"
)
current_version = int(run["version"])
if (
expected_version is not None
and current_version != expected_version
):
raise OptimisticConcurrencyError(
f"run_id {run_id!r} expected version {expected_version}, "
f"found {current_version}"
)
sequence = int(
connection.execute(
"SELECT COALESCE(MAX(sequence), 0) + 1 FROM run_events WHERE run_id = ?",
(run_id,),
).fetchone()[0]
)
connection.execute(
"""
INSERT INTO run_events(
event_id, run_id, sequence, run_version, event_type,
payload_json, legacy_step, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(
draft.event_id,
run_id,
sequence,
current_version + 1,
draft.event_type,
encoded_payload,
draft.legacy_step,
draft.created_at,
),
)
self._allocate_project_history_cursor_in_transaction(
connection,
project_id=str(run["project_id"]),
event_id=draft.event_id,
created_at=draft.created_at,
)
connection.execute(
"""
INSERT INTO run_event_sync_outbox(
event_id, run_id, run_sequence, status, attempt_count,
next_attempt_at, last_error, created_at, updated_at
) VALUES (?, ?, ?, 'pending', 0, ?, NULL, ?, ?)
""",
(
draft.event_id,
run_id,
sequence,
draft.created_at,
draft.created_at,
draft.created_at,
),
)
updated = connection.execute(
"""UPDATE runs
SET version = version + 1,
updated_at = ?,
status = CASE WHEN ? THEN ? ELSE status END,
active_attempt_id = CASE
WHEN ? THEN NULL
WHEN ? THEN ?
ELSE active_attempt_id
END
WHERE run_id = ? AND version = ?""",
(
draft.created_at,
run_status is not None,
run_status,
clear_active_attempt,
not clear_active_attempt and active_attempt_id is not None,
active_attempt_id,
run_id,
current_version,
),
)
if updated.rowcount != 1:
raise OptimisticConcurrencyError(
f"run_id {run_id!r} changed while appending event"
)
if run_status is not None and run_status in {
"interrupted",
"completed",
"failed",
"cancelled",
}:
connection.execute(
"DELETE FROM project_run_execution_leases WHERE run_id = ?",
(run_id,),
)
if draft.payload.get("advance_project_state", True) is not False:
self._refresh_project_execution_state_in_transaction(
connection,
run_id=run_id,
terminal_status=run_status,
terminal_payload=dict(draft.payload),
updated_at=draft.created_at,
)
return CommittedRunEvent(
event_id=draft.event_id,
run_id=run_id,
sequence=sequence,
event_type=draft.event_type,
payload=dict(draft.payload),
legacy_step=draft.legacy_step,
created_at=draft.created_at,
run_version=current_version + 1,
)
@staticmethod
def _insert_model_invocation_event(
connection: sqlite3.Connection,
*,
invocation_id: str,
event_type: str,
payload: dict[str, Any],
created_at: float,
) -> None:
event_index = int(
connection.execute(
"""
SELECT COALESCE(MAX(event_index), 0) + 1
FROM model_invocation_events WHERE invocation_id = ?
""",
(invocation_id,),
).fetchone()[0]
)
connection.execute(
"""
INSERT INTO model_invocation_events(
event_id, invocation_id, event_index, event_type,
payload_json, created_at
) VALUES (?, ?, ?, ?, ?, ?)
""",
(
f"model-invocation-event:{invocation_id}:{event_index}",
invocation_id,
event_index,
event_type,
canonical_json(payload),
created_at,
),
)
@staticmethod
def _allocate_project_history_cursor_in_transaction(
connection: sqlite3.Connection,
*,
project_id: str,
event_id: str,
created_at: float,
) -> int:
existing = connection.execute(
"""
SELECT journal_cursor FROM project_history_events
WHERE event_id = ?
""",
(event_id,),
).fetchone()
if existing is not None:
return int(existing["journal_cursor"])
cursor_row = connection.execute(
"""
SELECT next_cursor FROM project_history_cursors
WHERE project_id = ?
""",
(project_id,),
).fetchone()
if cursor_row is None:
cursor = 1
connection.execute(
"""
INSERT INTO project_history_cursors(
project_id, next_cursor, updated_at
) VALUES (?, 2, ?)
""",
(project_id, created_at),
)
else:
cursor = int(cursor_row["next_cursor"])
connection.execute(
"""
UPDATE project_history_cursors
SET next_cursor = ?, updated_at = ?
WHERE project_id = ?
""",
(cursor + 1, created_at, project_id),
)
connection.execute(
"""
INSERT INTO project_history_events(
project_id, journal_cursor, event_id, source_kind, created_at
) VALUES (?, ?, ?, 'native', ?)
""",
(project_id, cursor, event_id, created_at),
)
return cursor
def _migrate(self) -> None:
version = int(
self._connection.execute("PRAGMA user_version").fetchone()[0]
)
if version > SCHEMA_VERSION:
raise UnsupportedSchemaVersionError(
f"RunJournal schema {version} is newer than supported "
f"version {SCHEMA_VERSION}"
)
if version < 1:
self._connection.executescript(_MIGRATION_V1)
if version < 2:
self._connection.executescript(_MIGRATION_V2)
if version < 3:
self._connection.executescript(_MIGRATION_V3)
if version < 4:
self._connection.executescript(_MIGRATION_V4)
if version < 5:
self._connection.executescript(_MIGRATION_V5)
if version < 6:
self._connection.executescript(_MIGRATION_V6)
if version < 7:
self._connection.executescript(_MIGRATION_V7)
if version < 8:
self._connection.executescript(_MIGRATION_V8)
if version < 9:
self._connection.executescript(_MIGRATION_V9)
if version < 10:
self._connection.executescript(_MIGRATION_V10)
if version < 11:
self._connection.executescript(_MIGRATION_V11)
if version < 12:
self._connection.executescript(_MIGRATION_V12)
if version < 13:
self._connection.executescript(_MIGRATION_V13)
if version < 14:
self._connection.executescript(_MIGRATION_V14)
if version < 15:
self._connection.executescript(_MIGRATION_V15)
if version < 16:
self._connection.executescript(_MIGRATION_V16)
if version < 17:
self._connection.executescript(_MIGRATION_V17)
if version < 18:
self._connection.executescript(_MIGRATION_V18)
if version < 19:
self._connection.executescript(_MIGRATION_V19)
if version < 20:
self._connection.executescript(_MIGRATION_V20)
if version < 21:
migration = _MIGRATION_V21
follow_up_columns = {
row["name"]
for row in self._connection.execute(
"PRAGMA table_info(follow_up_requests)"
).fetchall()
}
if "source" in follow_up_columns:
migration = migration.replace(
"""ALTER TABLE follow_up_requests
ADD COLUMN source TEXT NOT NULL DEFAULT 'local'
CHECK(source IN ('local', 'remote_control', 'scheduled'));
""",
"",
)
if "source_command_id" in follow_up_columns:
migration = migration.replace(
"""ALTER TABLE follow_up_requests ADD COLUMN source_command_id TEXT;
""",
"",
)
self._connection.executescript(migration)
if version < 22:
self._connection.executescript(_MIGRATION_V22)
if version < 23:
columns = {
row["name"]
for row in self._connection.execute(
"PRAGMA table_info(memory_mutation_outbox)"
).fetchall()
}
migration = _MIGRATION_V23
if "payload_json" in columns:
migration = migration.replace(
"ALTER TABLE memory_mutation_outbox\n"
"ADD COLUMN payload_json TEXT NOT NULL DEFAULT '{}';\n",
"",
)
if "scope_revision" in columns:
migration = migration.replace(
"ALTER TABLE memory_mutation_outbox\n"
"ADD COLUMN scope_revision INTEGER NOT NULL DEFAULT 0;\n",
"",
)
self._connection.executescript(migration)
if version < 24:
self._connection.executescript(_MIGRATION_V24)
if version < 25:
self._connection.executescript(_MIGRATION_V25)
if version < 26:
self._connection.executescript(_MIGRATION_V26)
if version < 27:
self._connection.executescript(_MIGRATION_V27)
if version < 28:
inbox_columns = {
row["name"]
for row in self._connection.execute(
"PRAGMA table_info(remote_command_inbox)"
).fetchall()
}
migration = _MIGRATION_V28
if "delivery_lease_token" in inbox_columns:
migration = migration.replace(
"ALTER TABLE remote_command_inbox "
"ADD COLUMN delivery_lease_token TEXT;\n",
"",
)
self._connection.executescript(migration)
if version < 29:
self._connection.executescript(_MIGRATION_V29)
if version < 30:
self._connection.executescript(_MIGRATION_V30)
if version < 31:
self._connection.executescript(_MIGRATION_V31)
if version < 32:
self._connection.executescript(_MIGRATION_V32)
@contextmanager
def _write_transaction(self) -> Iterator[sqlite3.Connection]:
with self._lock:
self._connection.execute("BEGIN IMMEDIATE")
try:
yield self._connection
except BaseException:
self._connection.rollback()
raise
else:
self._connection.commit()
@contextmanager
def _savepoint(
self,
connection: sqlite3.Connection,
prefix: str,
) -> Iterator[None]:
name = f"{prefix}_{uuid.uuid4().hex}"
connection.execute(f"SAVEPOINT {name}")
try:
yield
except BaseException:
connection.execute(f"ROLLBACK TO SAVEPOINT {name}")
connection.execute(f"RELEASE SAVEPOINT {name}")
raise
else:
connection.execute(f"RELEASE SAVEPOINT {name}")
def _resolve_duplicate_event(
self,
connection: sqlite3.Connection,
row: sqlite3.Row,
*,
run_id: str,
draft: RunEventDraft,
payload_json: str,
) -> CommittedRunEvent:
if (
row["run_id"] != run_id
or row["event_type"] != draft.event_type
or row["payload_json"] != payload_json
or row["legacy_step"] != draft.legacy_step
):
raise IdempotencyConflictError(
f"event_id {draft.event_id!r} was reused with different data"
)
return self._event_from_row(row)
@staticmethod
def _assert_batch_lease(
connection: sqlite3.Connection,
batch: RunEventSyncBatch,
event_ids: list[str],
) -> None:
if not event_ids:
raise ValueError("outbox batch must contain events")
count = int(
connection.execute(
"""
SELECT COUNT(*)
FROM run_event_sync_outbox
WHERE status = 'sending' AND lease_token = ?
AND event_id IN (SELECT value FROM json_each(?))
""",
(
batch.lease_token,
json.dumps(event_ids, separators=(",", ":")),
),
).fetchone()[0]
)
if count != len(event_ids):
raise OutboxLeaseLostError(
f"outbox lease for {batch.run_id!r} is stale"
)
@staticmethod
def _assert_command_batch_lease(
connection: sqlite3.Connection,
batch: CommandResultSyncBatch,
event_ids: list[str],
) -> None:
if not event_ids:
raise ValueError("command outbox batch must contain events")
count = int(
connection.execute(
"""
SELECT COUNT(*) FROM command_result_outbox
WHERE status = 'sending' AND lease_token = ?
AND event_id IN (SELECT value FROM json_each(?))
""",
(
batch.lease_token,
json.dumps(event_ids, separators=(",", ":")),
),
).fetchone()[0]
)
if count != len(event_ids):
raise OutboxLeaseLostError(
f"command outbox lease for {batch.command_id!r} is stale"
)
@staticmethod
def _tool_call_requires_fail_closed(tool: sqlite3.Row) -> bool:
safety = ToolSafetyClass(tool["safety_class"])
return not automatic_tool_replay_allowed(
safety,
idempotency_key=tool["idempotency_key"],
)
@staticmethod
def _unsafe_resume_blockers(
connection: sqlite3.Connection, run_id: str
) -> list[str]:
rows = connection.execute(
"""
SELECT tool_call_id, safety_class, idempotency_key
FROM tool_calls
WHERE run_id = ?
AND status IN ('dispatched', 'timed_out', 'outcome_unknown')
ORDER BY created_at
""",
(run_id,),
).fetchall()
return [
row["tool_call_id"]
for row in rows
if SQLiteRunJournal._tool_call_requires_fail_closed(row)
]
def _finish_command_result_batch(
self,
batch: CommandResultSyncBatch,
status: str,
*,
error: str | None = None,
next_attempt_at: float | None = None,
increment_attempt: bool = False,
now: float | None = None,
) -> None:
timestamp = now if now is not None else time.time()
event_ids = [event.event_id for event in batch.events]
with self._write_transaction() as connection:
self._assert_command_batch_lease(connection, batch, event_ids)
connection.execute(
"""
UPDATE command_result_outbox
SET status = ?,
attempt_count = attempt_count + ?,
next_attempt_at = COALESCE(?, next_attempt_at),
lease_token = NULL, lease_until = NULL,
last_error = ?, updated_at = ?
WHERE event_id IN (SELECT value FROM json_each(?))
""",
(
status,
int(increment_attempt),
next_attempt_at,
error[:4000] if error else None,
timestamp,
json.dumps(event_ids, separators=(",", ":")),
),
)
@staticmethod
def _run_from_row(row: sqlite3.Row) -> RunRecord:
return RunRecord(
run_id=row["run_id"],
project_id=row["project_id"],
status=row["status"],
version=int(row["version"]),
active_attempt_id=row["active_attempt_id"],
deadline_at=row["deadline_at"],
timeout_policy_version=row["timeout_policy_version"],
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
parent_run_id=row["parent_run_id"],
timeout_policy=json.loads(row["timeout_policy_json"] or "{}"),
cancel_request_id=row["cancel_request_id"],
cancel_requested_at=(
float(row["cancel_requested_at"])
if row["cancel_requested_at"] is not None
else None
),
origin=row["origin"],
resume_blocked_reason=row["resume_blocked_reason"],
)
@staticmethod
def _cloud_restored_status(status: str) -> str:
if status in {"completed", "failed", "cancelled"}:
return status
return "interrupted"
@staticmethod
def _attempt_from_row(row: sqlite3.Row) -> RunAttemptRecord:
return RunAttemptRecord(
attempt_id=row["attempt_id"],
run_id=row["run_id"],
attempt_number=int(row["attempt_number"]),
status=row["status"],
started_at=float(row["started_at"]),
ended_at=(
float(row["ended_at"]) if row["ended_at"] is not None else None
),
outcome=row["outcome"],
timeout_reason=row["timeout_reason"],
resume_request_id=row["resume_request_id"],
resume_reason=row["resume_reason"],
policy_version=row["policy_version"],
elapsed_active_ms=int(row["elapsed_active_ms"]),
last_consumer_heartbeat_at=(
float(row["last_consumer_heartbeat_at"])
if row["last_consumer_heartbeat_at"] is not None
else None
),
environment_spec_id=row["environment_spec_id"],
environment_spec_digest=row["environment_spec_digest"],
bundle_revision_id=row["bundle_revision_id"],
permission_profile_revision=row["permission_profile_revision"],
thinking_effort_requested=row["thinking_effort_requested"],
thinking_effort_effective=row["thinking_effort_effective"],
provider_capability_revision=row["provider_capability_revision"],
)
@staticmethod
def _attempt_environment_values(
environment: AttemptEnvironmentBinding | None,
) -> tuple[str | None, ...]:
if environment is None:
return (None, None, None, None, None, None, None)
required = (
environment.environment_spec_id,
environment.environment_spec_digest,
environment.bundle_revision_id,
environment.permission_profile_revision,
environment.provider_capability_revision,
)
if any(not value.strip() for value in required):
raise ValueError("Attempt environment binding fields are required")
for value in (
environment.thinking_effort_requested,
environment.thinking_effort_effective,
):
try:
ThinkingEffort(value)
except ValueError as exc:
raise ValueError(
f"invalid persisted thinking effort {value!r}"
) from exc
return (
environment.environment_spec_id,
environment.environment_spec_digest,
environment.bundle_revision_id,
environment.permission_profile_revision,
environment.thinking_effort_requested,
environment.thinking_effort_effective,
environment.provider_capability_revision,
)
@staticmethod
def _workspace_config_revision_from_row(
row: sqlite3.Row,
) -> WorkspaceConfigRevisionRecord:
return WorkspaceConfigRevisionRecord(
revision_id=row["revision_id"],
bundle_id=row["bundle_id"],
revision_number=int(row["revision_number"]),
status=row["status"],
version=int(row["version"]),
manifest=json.loads(row["manifest_json"]),
manifest_digest=row["manifest_digest"],
created_by=row["created_by"],
created_at=float(row["created_at"]),
)
@staticmethod
def _workspace_config_materialization_from_row(
row: sqlite3.Row,
) -> WorkspaceConfigMaterializationRecord:
return WorkspaceConfigMaterializationRecord(
materialization_id=row["materialization_id"],
space_id=row["space_id"],
revision_id=row["revision_id"],
config_placement=row["config_placement"],
state=row["state"],
local_override_digest=row["local_override_digest"],
materialized_at=(
float(row["materialized_at"])
if row["materialized_at"] is not None
else None
),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _workspace_config_draft_from_row(
row: sqlite3.Row,
) -> WorkspaceConfigDraftRecord:
return WorkspaceConfigDraftRecord(
space_id=row["space_id"],
version=int(row["version"]),
base_revision_id=row["base_revision_id"],
document=json.loads(row["document_json"]),
document_digest=row["document_digest"],
updated_by=row["updated_by"],
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _workspace_config_draft_asset_descriptor_from_row(
row: sqlite3.Row,
) -> WorkspaceConfigDraftAssetDescriptorRecord:
return WorkspaceConfigDraftAssetDescriptorRecord(
space_id=row["space_id"],
draft_version=int(row["draft_version"]),
document_digest=row["document_digest"],
logical_path=row["logical_path"],
content_digest=row["content_digest"],
media_type=row["media_type"],
size_bytes=int(row["size_bytes"]),
executable=bool(row["executable"]),
provenance=row["provenance"],
created_at=float(row["created_at"]),
)
@staticmethod
def _workspace_config_draft_from_import_request(
row: sqlite3.Row,
) -> WorkspaceConfigDraftRecord:
return WorkspaceConfigDraftRecord(
space_id=row["space_id"],
version=int(row["result_version"]),
base_revision_id=row["result_base_revision_id"],
document=json.loads(row["result_document_json"]),
document_digest=row["result_document_digest"],
updated_by=row["result_updated_by"],
created_at=float(row["result_created_at"]),
updated_at=float(row["result_updated_at"]),
)
@staticmethod
def _workspace_bundle_install_proposal_from_row(
row: sqlite3.Row,
) -> WorkspaceBundleInstallProposalRecord:
return WorkspaceBundleInstallProposalRecord(
proposal_id=row["proposal_id"],
request_id=row["request_id"],
space_id=row["space_id"],
bundle_id=row["bundle_id"],
revision_id=row["revision_id"],
config_placement=row["config_placement"],
state=row["state"],
version=int(row["version"]),
manifest=json.loads(row["manifest_json"]),
manifest_digest=row["manifest_digest"],
assets=tuple(json.loads(row["assets_json"])),
install_plan=json.loads(row["install_plan_json"]),
decided_by=row["decided_by"],
decided_at=(
float(row["decided_at"])
if row["decided_at"] is not None
else None
),
error_code=row["error_code"],
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _workspace_bundle_local_binding_from_row(
row: sqlite3.Row,
) -> WorkspaceBundleLocalBindingRecord:
return WorkspaceBundleLocalBindingRecord(
binding_id=row["binding_id"],
proposal_id=row["proposal_id"],
slot_id=row["slot_id"],
binding_kind=row["binding_kind"],
connector_id=row["connector_id"],
opaque_connection_id=row["opaque_connection_id"],
local_path=row["local_path"],
required_grants=tuple(json.loads(row["required_grants_json"])),
authorized_by=row["authorized_by"],
authorized_at=float(row["authorized_at"]),
)
@staticmethod
def _workspace_bundle_secret_binding_from_row(
row: sqlite3.Row,
) -> WorkspaceBundleSecretBindingRecord:
return WorkspaceBundleSecretBindingRecord(
binding_id=row["binding_id"],
proposal_id=row["proposal_id"],
requirement_key=row["requirement_key"],
requirement_kind=row["requirement_kind"],
binding_version=int(row["binding_version"]),
secret_ref=row["secret_ref"],
account_scope_digest=row["account_scope_digest"],
authorized_by=row["authorized_by"],
authorized_at=float(row["authorized_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _effective_environment_spec_from_row(
row: sqlite3.Row,
) -> EffectiveEnvironmentSpecRecord:
return EffectiveEnvironmentSpecRecord(
environment_spec_id=row["environment_spec_id"],
owner_type=row["owner_type"],
owner_id=row["owner_id"],
bundle_revision_id=row["bundle_revision_id"],
manifest_digest=row["manifest_digest"],
spec=json.loads(row["spec_json"]),
environment_spec_digest=row["environment_spec_digest"],
semantic_spec_digest=row["semantic_spec_digest"],
local_materialization_digest=row["local_materialization_digest"],
redacted_spec=json.loads(row["redacted_spec_json"]),
projection_digest=row["projection_digest"],
permission_profile_revision=row["permission_profile_revision"],
provider_capability_revision=row["provider_capability_revision"],
created_at=float(row["created_at"]),
)
@staticmethod
def _git_repository_from_row(row: sqlite3.Row) -> GitRepositoryRecord:
return GitRepositoryRecord(
repository_id=row["repository_id"],
space_id=row["space_id"],
repository_role=row["repository_role"],
root_path=row["root_path"],
root_path_digest=row["root_path_digest"],
ownership=row["ownership"],
state=row["state"],
version_coverage=row["version_coverage"],
hooks_mode=row["hooks_mode"],
repo_subdir=row["repo_subdir"],
version=int(row["version"]),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _git_operation_from_row(row: sqlite3.Row) -> GitOperationRecord:
return GitOperationRecord(
operation_id=row["operation_id"],
repository_id=row["repository_id"],
request_id=row["request_id"],
operation_type=row["operation_type"],
payload_digest=row["payload_digest"],
status=row["status"],
expected_repo_state_digest=row["expected_repo_state_digest"],
observed_repo_state_digest=row["observed_repo_state_digest"],
result=(
json.loads(row["result_json"])
if row["result_json"] is not None
else None
),
error_code=row["error_code"],
error_message=row["error_message"],
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _git_checkpoint_from_row(row: sqlite3.Row) -> GitCheckpointRecord:
return GitCheckpointRecord(
checkpoint_id=row["checkpoint_id"],
repository_id=row["repository_id"],
operation_id=row["operation_id"],
target_role=row["target_role"],
target_id=row["target_id"],
commit_oid=row["commit_oid"],
parent_oid=row["parent_oid"],
paths=tuple(json.loads(row["paths_json"])),
actor_id=row["actor_id"],
trigger=row["trigger"],
message=row["message"],
created_at=float(row["created_at"]),
)
@staticmethod
def _project_git_state_from_row(
row: sqlite3.Row,
) -> ProjectGitStateRecord:
return ProjectGitStateRecord(
project_id=row["project_id"],
repository_id=row["repository_id"],
integration_ref=row["integration_ref"],
integration_head=row["integration_head"],
last_synced_user_head=row["last_synced_user_head"],
pending_apply=bool(row["pending_apply"]),
worktree_path=row["worktree_path"],
projected_head=row["projected_head"],
state=row["state"],
version=int(row["version"]),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _run_git_materialization_from_row(
row: sqlite3.Row,
) -> RunGitMaterializationRecord:
return RunGitMaterializationRecord(
run_id=row["run_id"],
project_id=row["project_id"],
repository_id=row["repository_id"],
workspace_base_ref=row["workspace_base_ref"],
workspace_base_commit=row["workspace_base_commit"],
project_state_version=int(row["project_state_version"]),
materialization_state=row["materialization_state"],
run_ref=row["run_ref"],
worktree_path=row["worktree_path"],
promoted_commit=row["promoted_commit"],
version=int(row["version"]),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _git_agent_workspace_from_row(
row: sqlite3.Row,
) -> GitAgentWorkspaceRecord:
return GitAgentWorkspaceRecord(
workspace_id=row["workspace_id"],
run_id=row["run_id"],
repository_id=row["repository_id"],
agent_id=row["agent_id"],
agent_ref=row["agent_ref"],
worktree_path=row["worktree_path"],
base_commit=row["base_commit"],
head_commit=row["head_commit"],
state=row["state"],
lease_owner=row["lease_owner"],
lease_token=row["lease_token"],
lease_until=(
float(row["lease_until"])
if row["lease_until"] is not None
else None
),
last_operation_id=row["last_operation_id"],
conflict_interaction_id=row["conflict_interaction_id"],
version=int(row["version"]),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _workspace_read_snapshot_from_row(
row: sqlite3.Row,
) -> WorkspaceReadSnapshotRecord:
return WorkspaceReadSnapshotRecord(
snapshot_id=row["snapshot_id"],
run_id=row["run_id"],
project_id=row["project_id"],
repository_id=row["repository_id"],
generation=int(row["generation"]),
project_base_commit=row["project_base_commit"],
common_base_commit=row["common_base_commit"],
project_state_version=int(row["project_state_version"]),
snapshot_ref=row["snapshot_ref"],
user_head=row["user_head"],
user_working_state_digest=row["user_working_state_digest"],
overlay_manifest_digest=row["overlay_manifest_digest"],
state=row["state"],
expires_at=(
float(row["expires_at"])
if row["expires_at"] is not None
else None
),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _workspace_overlay_entry_from_row(
row: sqlite3.Row,
) -> WorkspaceOverlayEntryRecord:
return WorkspaceOverlayEntryRecord(
snapshot_id=row["snapshot_id"],
relative_path=row["relative_path"],
source_kind=row["source_kind"],
entry_state=row["entry_state"],
source_token=json.loads(row["source_token_json"]),
project_blob_oid=row["project_blob_oid"],
materialized_content_digest=row["materialized_content_digest"],
preimage_cache_key=row["preimage_cache_key"],
size_bytes=int(row["size_bytes"]),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _workspace_snapshot_range_from_row(
row: sqlite3.Row,
) -> WorkspaceSnapshotRangeRecord:
return WorkspaceSnapshotRangeRecord(
snapshot_id=row["snapshot_id"],
relative_path=row["relative_path"],
start_offset=int(row["start_offset"]),
end_offset=int(row["end_offset"]),
content_digest=row["content_digest"],
cache_key=row["cache_key"],
created_at=float(row["created_at"]),
)
@staticmethod
def _git_change_set_from_row(row: sqlite3.Row) -> GitChangeSetRecord:
return GitChangeSetRecord(
change_set_id=row["change_set_id"],
run_id=row["run_id"],
repository_id=row["repository_id"],
worktree_ref=row["worktree_ref"],
base_commit=row["base_commit"],
state=row["state"],
version=int(row["version"]),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _git_change_set_item_from_row(
row: sqlite3.Row,
) -> GitChangeSetItemRecord:
return GitChangeSetItemRecord(
change_set_id=row["change_set_id"],
relative_path=row["relative_path"],
operation_request_id=row["operation_request_id"],
actor_id=row["actor_id"],
trigger=row["trigger"],
change_kind=row["change_kind"],
source=row["source"],
preimage_digest=row["preimage_digest"],
result_digest=row["result_digest"],
size_bytes=(
int(row["size_bytes"])
if row["size_bytes"] is not None
else None
),
item_state=row["item_state"],
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _git_mutation_intent_from_row(
row: sqlite3.Row,
) -> GitMutationIntentRecord:
return GitMutationIntentRecord(
intent_id=row["intent_id"],
change_set_id=row["change_set_id"],
operation_request_id=row["operation_request_id"],
mutation_scope=row["mutation_scope"],
relative_path=row["relative_path"],
preimage_digest=row["preimage_digest"],
actor_id=row["actor_id"],
trigger=row["trigger"],
status=row["status"],
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _tool_call_from_row(row: sqlite3.Row) -> ToolCallRecord:
return ToolCallRecord(
tool_call_id=row["tool_call_id"],
run_id=row["run_id"],
attempt_id=row["attempt_id"],
tool_name=row["tool_name"],
status=row["status"],
safety_class=row["safety_class"],
idempotency_key=row["idempotency_key"],
request=json.loads(row["request_json"] or "{}"),
result=(
json.loads(row["result_json"]) if row["result_json"] else None
),
outcome=row["outcome"],
timeout_reason=row["timeout_reason"],
prepared_at=(
float(row["prepared_at"])
if row["prepared_at"] is not None
else None
),
dispatched_at=(
float(row["dispatched_at"])
if row["dispatched_at"] is not None
else None
),
completed_at=(
float(row["completed_at"])
if row["completed_at"] is not None
else None
),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _approval_from_row(row: sqlite3.Row) -> ApprovalRecord:
return ApprovalRecord(
approval_id=row["approval_id"],
run_id=row["run_id"],
attempt_id=row["attempt_id"],
status=row["status"],
prompt=json.loads(row["prompt_json"]),
decision=(
json.loads(row["decision_json"])
if row["decision_json"] is not None
else None
),
version=int(row["version"]),
expires_at=(
float(row["expires_at"])
if row["expires_at"] is not None
else None
),
expiry_action=row["expiry_action"],
created_at=float(row["created_at"]),
resolved_at=(
float(row["resolved_at"])
if row["resolved_at"] is not None
else None
),
action_digest=row["action_digest"],
policy_revision=row["policy_revision"],
safety_class=row["safety_class"],
decision_scope=row["decision_scope"],
)
@staticmethod
def _human_interaction_from_row(
row: sqlite3.Row,
) -> HumanInteractionRecord:
return HumanInteractionRecord(
interaction_id=row["interaction_id"],
run_id=row["run_id"],
attempt_id=row["attempt_id"],
interaction_type=row["interaction_type"],
status=row["status"],
request=json.loads(row["request_json"]),
response_schema=json.loads(row["response_schema_json"]),
requested_by=row["requested_by"],
version=int(row["version"]),
expires_at=(
float(row["expires_at"])
if row["expires_at"] is not None
else None
),
presented_at=(
float(row["presented_at"])
if row["presented_at"] is not None
else None
),
resolved_at=(
float(row["resolved_at"])
if row["resolved_at"] is not None
else None
),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _human_interaction_option_from_row(
row: sqlite3.Row,
) -> HumanInteractionOptionRecord:
return HumanInteractionOptionRecord(
interaction_id=row["interaction_id"],
option_id=row["option_id"],
position=int(row["position"]),
label=row["label"],
value=json.loads(row["value_json"]),
description=row["description"],
)
@staticmethod
def _human_interaction_decision_from_row(
row: sqlite3.Row,
) -> HumanInteractionDecisionRecord:
return HumanInteractionDecisionRecord(
decision_id=row["decision_id"],
interaction_id=row["interaction_id"],
decision_request_id=row["decision_request_id"],
decision=json.loads(row["decision_json"]),
actor_type=row["actor_type"],
actor_id=row["actor_id"],
source=row["source"],
action_digest=row["action_digest"],
created_at=float(row["created_at"]),
)
@staticmethod
def _space_permission_profile_from_row(
row: sqlite3.Row,
) -> SpacePermissionProfileRecord:
return SpacePermissionProfileRecord(
space_id=row["space_id"],
profile_name=row["profile_name"],
sandbox_mode=row["sandbox_mode"],
approval_mode=row["approval_mode"],
reviewer_mode=row["reviewer_mode"],
revision=int(row["revision"]),
updated_by=row["updated_by"],
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _space_permission_profile_revision_from_row(
row: sqlite3.Row,
) -> SpacePermissionProfileRevisionRecord:
return SpacePermissionProfileRevisionRecord(
revision_id=row["revision_id"],
space_id=row["space_id"],
profile_name=row["profile_name"],
sandbox_mode=row["sandbox_mode"],
approval_mode=row["approval_mode"],
reviewer_mode=row["reviewer_mode"],
revision=int(row["revision"]),
created_by=row["created_by"],
created_at=float(row["created_at"]),
)
@staticmethod
def _approval_rule_from_row(row: sqlite3.Row) -> ApprovalRuleRecord:
return ApprovalRuleRecord(
rule_id=row["rule_id"],
space_id=row["space_id"],
effect=row["effect"],
action_pattern=row["action_pattern"],
resource_pattern=row["resource_pattern"],
scope=row["scope"],
run_id=row["run_id"],
source_interaction_id=row["source_interaction_id"],
expires_at=(
float(row["expires_at"])
if row["expires_at"] is not None
else None
),
created_by=row["created_by"],
created_at=float(row["created_at"]),
)
@staticmethod
def _security_audit_event_from_row(
row: sqlite3.Row,
) -> SecurityAuditEventRecord:
return SecurityAuditEventRecord(
audit_event_id=row["audit_event_id"],
space_id=row["space_id"],
run_id=row["run_id"],
interaction_id=row["interaction_id"],
event_type=row["event_type"],
actor_type=row["actor_type"],
actor_id=row["actor_id"],
action_digest=row["action_digest"],
details=json.loads(row["details_json"]),
created_at=float(row["created_at"]),
)
@staticmethod
def _validate_memory_scope(scope_type: str, scope_id: str) -> None:
if scope_type not in _MEMORY_SCOPE_TYPES:
raise ValueError("invalid Memory scope_type")
if not scope_id.strip() or len(scope_id) > 256:
raise ValueError("Memory scope_id is required and bounded")
@staticmethod
def _validate_memory_entry_values(
*,
content: str | None,
kind: str | None,
priority: str,
token_count: int | None,
created_by: str | None,
source_trust: str | None,
sensitivity: str,
allow_empty_content: bool = False,
) -> None:
if (
content is None
or (not allow_empty_content and not content.strip())
or len(content) > 8192
):
raise ValueError("Memory content is required and bounded")
if kind not in _MEMORY_KINDS:
raise ValueError("invalid Memory kind")
if priority not in {"normal", "high"}:
raise ValueError("invalid Memory priority")
if token_count is None or token_count < 1:
raise ValueError("Memory token_count must be positive")
if created_by not in {"extractor", "agent", "user", "importer"}:
raise ValueError("invalid Memory created_by")
if source_trust not in _MEMORY_SOURCE_TRUST:
raise ValueError("invalid Memory source_trust")
if sensitivity not in {"normal", "personal", "sensitive"}:
raise ValueError("invalid Memory sensitivity")
@staticmethod
def _ensure_memory_scope_state_in_transaction(
connection: sqlite3.Connection,
*,
scope_type: str,
scope_id: str,
owner_kind: str,
token_limit: int,
now: float,
) -> sqlite3.Row:
connection.execute(
"""
INSERT OR IGNORE INTO memory_scope_state(
scope_type, scope_id, owner_kind, revision,
capture_enabled, use_enabled, sync_scope, token_limit,
current_token_count, consolidate_threshold,
extractor_version, updated_at
) VALUES (?, ?, ?, 0, ?, 1, 'full_memory', ?, 0, 0.75,
'memory-v2', ?)
""",
(
scope_type,
scope_id,
owner_kind,
1,
token_limit,
now,
),
)
row = connection.execute(
"""
SELECT * FROM memory_scope_state
WHERE scope_type = ? AND scope_id = ?
""",
(scope_type, scope_id),
).fetchone()
assert row is not None
if row["owner_kind"] != owner_kind:
raise InvalidRunTransitionError(
f"Memory scope is owned by {row['owner_kind']!r}, not {owner_kind!r}"
)
return row
@staticmethod
def _assert_memory_capacity(state: sqlite3.Row, token_delta: int) -> None:
resulting = int(state["current_token_count"]) + token_delta
limit = int(state["token_limit"])
if resulting < 0:
raise RunJournalError(
"Memory token accounting would become negative"
)
if resulting > limit:
raise InvalidRunTransitionError(
f"Memory capacity exceeded by {resulting - limit} tokens"
)
@staticmethod
def _memory_entry_digest(row: sqlite3.Row) -> str:
payload = {
"memory_id": row["memory_id"],
"scope_type": row["scope_type"],
"scope_id": row["scope_id"],
"kind": row["kind"],
"content": row["content"],
"priority": row["priority"],
"version": int(row["version"]),
"token_count": int(row["token_count"]),
"pinned_by_user": bool(row["pinned_by_user"]),
"confirmed_by_user": bool(row["confirmed_by_user"]),
"created_by": row["created_by"],
"source_trust": row["source_trust"],
"sensitivity": row["sensitivity"],
"source_refs": json.loads(row["source_refs_json"]),
"deleted_at": row["deleted_at"],
}
return hashlib.sha256(
json.dumps(
payload,
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
).encode("utf-8")
).hexdigest()
@staticmethod
def _memory_scope_state_from_row(
row: sqlite3.Row,
) -> MemoryScopeStateRecord:
return MemoryScopeStateRecord(
scope_type=row["scope_type"],
scope_id=row["scope_id"],
owner_kind=row["owner_kind"],
revision=int(row["revision"]),
capture_enabled=bool(row["capture_enabled"]),
use_enabled=bool(row["use_enabled"]),
sync_scope=row["sync_scope"],
token_limit=int(row["token_limit"]),
current_token_count=int(row["current_token_count"]),
consolidate_threshold=float(row["consolidate_threshold"]),
processed_through_watermark=row["processed_through_watermark"],
watermark_kind=row["watermark_kind"],
extractor_version=row["extractor_version"],
last_consolidated_at=(
float(row["last_consolidated_at"])
if row["last_consolidated_at"] is not None
else None
),
last_error=row["last_error"],
updated_at=float(row["updated_at"]),
)
@staticmethod
def _memory_entry_from_row(row: sqlite3.Row) -> MemoryEntryRecord:
return MemoryEntryRecord(
memory_id=row["memory_id"],
scope_type=row["scope_type"],
scope_id=row["scope_id"],
kind=row["kind"],
content=row["content"],
priority=row["priority"],
version=int(row["version"]),
token_count=int(row["token_count"]),
pinned_by_user=bool(row["pinned_by_user"]),
confirmed_by_user=bool(row["confirmed_by_user"]),
created_by=row["created_by"],
source_trust=row["source_trust"],
sensitivity=row["sensitivity"],
source_refs=tuple(json.loads(row["source_refs_json"])),
last_used_at=(
float(row["last_used_at"])
if row["last_used_at"] is not None
else None
),
usage_count=int(row["usage_count"]),
deleted_at=(
float(row["deleted_at"])
if row["deleted_at"] is not None
else None
),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _memory_mutation_from_row(row: sqlite3.Row) -> MemoryMutationRecord:
return MemoryMutationRecord(
mutation_id=row["mutation_id"],
memory_id=row["memory_id"],
scope_type=row["scope_type"],
scope_id=row["scope_id"],
operation=row["operation"],
expected_version=(
int(row["expected_version"])
if row["expected_version"] is not None
else None
),
before_hash=row["before_hash"],
after_hash=row["after_hash"],
actor_type=row["actor_type"],
actor_id=row["actor_id"],
run_id=row["run_id"],
activity_id=row["activity_id"],
reason=row["reason"],
source_refs=tuple(json.loads(row["source_refs_json"])),
idempotency_key=row["idempotency_key"],
request_digest=row["request_digest"],
decision_id=row["decision_id"],
created_at=float(row["created_at"]),
)
@staticmethod
def _memory_reconciliation_from_row(
row: sqlite3.Row,
) -> MemoryReconciliationRecord:
return MemoryReconciliationRecord(
reconciliation_id=row["reconciliation_id"],
account_owner_id=row["account_owner_id"],
scope_type=row["scope_type"],
scope_id=row["scope_id"],
memory_id=row["memory_id"],
local_entry=json.loads(row["local_entry_json"]),
cloud_entry=json.loads(row["cloud_entry_json"]),
status=row["status"],
created_at=float(row["created_at"]),
resolved_at=(
float(row["resolved_at"])
if row["resolved_at"] is not None
else None
),
)
@staticmethod
def _event_from_row(row: sqlite3.Row) -> CommittedRunEvent:
return CommittedRunEvent(
event_id=row["event_id"],
run_id=row["run_id"],
sequence=int(row["sequence"]),
event_type=row["event_type"],
payload=json.loads(row["payload_json"]),
legacy_step=row["legacy_step"],
created_at=float(row["created_at"]),
run_version=int(row["run_version"]),
)
@staticmethod
def _model_invocation_from_row(
row: sqlite3.Row,
) -> ModelInvocationRecord:
return ModelInvocationRecord(
invocation_id=row["invocation_id"],
run_id=row["run_id"],
attempt_id=row["attempt_id"],
agent_id=row["agent_id"],
logical_call_id=row["logical_call_id"],
retry_index=int(row["retry_index"]),
status=row["status"],
provider=row["provider"],
model=row["model"],
transport=row["transport"],
thinking_effort=row["thinking_effort"],
request=json.loads(row["request_json"]),
response=(
json.loads(row["response_json"])
if row["response_json"] is not None
else None
),
request_digest=row["request_digest"],
response_digest=row["response_digest"],
prompt_tokens=(
int(row["prompt_tokens"])
if row["prompt_tokens"] is not None
else None
),
completion_tokens=(
int(row["completion_tokens"])
if row["completion_tokens"] is not None
else None
),
cache_read_tokens=(
int(row["cache_read_tokens"])
if row["cache_read_tokens"] is not None
else None
),
cache_write_tokens=(
int(row["cache_write_tokens"])
if row["cache_write_tokens"] is not None
else None
),
finish_reason=row["finish_reason"],
error_code=row["error_code"],
error_message=row["error_message"],
redaction_version=row["redaction_version"],
started_at=float(row["started_at"]),
first_token_at=(
float(row["first_token_at"])
if row["first_token_at"] is not None
else None
),
completed_at=(
float(row["completed_at"])
if row["completed_at"] is not None
else None
),
)
@staticmethod
def _model_invocation_event_from_row(
row: sqlite3.Row,
) -> ModelInvocationEventRecord:
return ModelInvocationEventRecord(
event_id=row["event_id"],
invocation_id=row["invocation_id"],
event_index=int(row["event_index"]),
event_type=row["event_type"],
payload=json.loads(row["payload_json"]),
created_at=float(row["created_at"]),
)
@staticmethod
def _follow_up_request_from_row(
row: sqlite3.Row,
) -> FollowUpRequestRecord:
return FollowUpRequestRecord(
request_id=row["request_id"],
project_id=row["project_id"],
content=row["content"],
attachment_paths=tuple(json.loads(row["attachment_paths_json"])),
delivery_mode=row["delivery_mode"],
status=row["status"],
admitted_run_id=row["admitted_run_id"],
source=row["source"],
source_command_id=row["source_command_id"],
last_error=row["last_error"],
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _project_workspace_binding_from_row(
row: sqlite3.Row,
) -> ProjectWorkspaceBindingRecord:
return ProjectWorkspaceBindingRecord(
project_id=row["project_id"],
repository_id=row["repository_id"],
checkout_id=row["checkout_id"],
checkout_mode=row["checkout_mode"],
target_ref=row["target_ref"],
worktree_path=row["worktree_path"],
version=int(row["version"]),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _workspace_writer_lease_from_row(
row: sqlite3.Row,
) -> WorkspaceWriterLeaseRecord:
return WorkspaceWriterLeaseRecord(
repository_id=row["repository_id"],
checkout_id=row["checkout_id"],
request_id=row["request_id"],
task_id=row["task_id"],
project_id=row["project_id"],
target_ref=row["target_ref"],
acquired_at=float(row["acquired_at"]),
version=int(row["version"]),
)
@staticmethod
def _workspace_writer_request_from_row(
connection: sqlite3.Connection,
row: sqlite3.Row,
) -> WorkspaceWriterRequestRecord:
queue_position = None
blocker_task_id = None
if row["status"] == "queued":
position = connection.execute(
"""
SELECT COUNT(*) AS position
FROM workspace_writer_requests
WHERE repository_id = ? AND checkout_id = ?
AND status = 'queued'
AND (
created_at < ?
OR (created_at = ? AND request_id <= ?)
)
""",
(
row["repository_id"],
row["checkout_id"],
row["created_at"],
row["created_at"],
row["request_id"],
),
).fetchone()
assert position is not None
queue_position = int(position["position"])
lease = connection.execute(
"""
SELECT task_id FROM workspace_writer_leases
WHERE repository_id = ? AND checkout_id = ?
""",
(row["repository_id"], row["checkout_id"]),
).fetchone()
blocker_task_id = lease["task_id"] if lease is not None else None
return WorkspaceWriterRequestRecord(
request_id=row["request_id"],
repository_id=row["repository_id"],
checkout_id=row["checkout_id"],
task_id=row["task_id"],
project_id=row["project_id"],
target_ref=row["target_ref"],
reason=row["reason"],
status=row["status"],
queue_position=queue_position,
blocker_task_id=blocker_task_id,
created_at=float(row["created_at"]),
acquired_at=(
float(row["acquired_at"])
if row["acquired_at"] is not None
else None
),
finished_at=(
float(row["finished_at"])
if row["finished_at"] is not None
else None
),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _project_execution_state_from_row(
row: sqlite3.Row,
) -> ProjectExecutionStateRecord:
return ProjectExecutionStateRecord(
project_id=row["project_id"],
state_version=int(row["state_version"]),
frontier=(
json.loads(row["frontier_json"])
if row["frontier_json"] is not None
else None
),
frontier_digest=row["frontier_digest"],
frontier_run_id=row["frontier_run_id"],
updated_at=float(row["updated_at"]),
)
@staticmethod
def _continuation_claim_from_row(
row: sqlite3.Row,
) -> ContinuationClaimRecord:
return ContinuationClaimRecord(
fingerprint=row["fingerprint"],
request_id=row["request_id"],
project_id=row["project_id"],
project_state_version=int(row["project_state_version"]),
intent=row["intent"],
base_run_id=row["base_run_id"],
next_action=row["next_action"],
created_at=float(row["created_at"]),
)
@staticmethod
def _context_projection_diagnostic_from_row(
row: sqlite3.Row,
) -> ContextProjectionDiagnosticRecord:
return ContextProjectionDiagnosticRecord(
projection_id=row["projection_id"],
project_id=row["project_id"],
run_id=row["run_id"],
source_event_ids=tuple(json.loads(row["source_event_ids_json"])),
source_memory_ids=tuple(json.loads(row["source_memory_ids_json"])),
project_state_version=int(row["project_state_version"]),
projection_digest=row["projection_digest"],
token_count=int(row["token_count"]),
created_at=float(row["created_at"]),
)
@staticmethod
def _command_from_row(row: sqlite3.Row) -> RemoteCommandInboxRecord:
return RemoteCommandInboxRecord(
command_id=row["command_id"],
session_id=row["session_id"],
user_id=int(row["user_id"]),
project_id=row["project_id"],
run_id=row["run_id"],
route_version=int(row["route_version"]),
command_type=row["command_type"],
payload=json.loads(row["payload_json"]),
expires_at=float(row["expires_at"]),
receipt_grace_until=float(row["receipt_grace_until"]),
requires_online_receipt_confirmation=bool(
row["requires_online_receipt_confirmation"]
),
delivery_lease_token=row["delivery_lease_token"],
receipt_event_id=row["receipt_event_id"],
receipt_status=row["receipt_status"],
state=row["state"],
dispatch_attempt_count=int(row["dispatch_attempt_count"]),
last_error=row["last_error"],
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)
@staticmethod
def _command_event_from_row(row: sqlite3.Row) -> CommandResultEvent:
return CommandResultEvent(
event_id=row["event_id"],
command_id=row["command_id"],
command_event_sequence=int(row["command_event_sequence"]),
event_type=row["event_type"],
payload=json.loads(row["payload_json"]),
occurred_at=float(row["occurred_at"]),
)
@staticmethod
def _outbox_from_row(row: sqlite3.Row) -> RunEventSyncOutboxRecord:
return RunEventSyncOutboxRecord(
event_id=row["event_id"],
run_id=row["run_id"],
run_sequence=int(row["run_sequence"]),
status=row["status"],
attempt_count=int(row["attempt_count"]),
next_attempt_at=float(row["next_attempt_at"]),
last_error=row["last_error"],
lease_token=row["lease_token"],
lease_until=(
float(row["lease_until"])
if row["lease_until"] is not None
else None
),
created_at=float(row["created_at"]),
updated_at=float(row["updated_at"]),
)