Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions BUILD_INFO.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Build Information
Code Hash: 39d5fa3029ce
Build Time: 2025-11-23T20:28:47.366020
Git Commit: 98aed22eba81664cadd2d0b27cec747f9e119b90
Git Branch: release/1.6.5.1
Code Hash: 0ba4082fd4e1
Build Time: 2025-11-24T19:39:31.220745
Git Commit: 1b61879470b757e40c3065c7ae486cff5a108157
Git Branch: bugfix/issue-514-deferral-ui

This hash is a SHA-256 of all Python source files in the repository.
It provides a deterministic version identifier based on the actual code content.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

**A type-safe, auditable AI agent framework with built-in ethical reasoning**

**BETA RELEASE 1.6.5.1-stable** | [Release Notes](CHANGELOG.md) | [Documentation Hub](docs/README.md)
**BETA RELEASE 1.6.5.3-stable** | [Release Notes](CHANGELOG.md) | [Documentation Hub](docs/README.md)

Academic paper https://zenodo.org/records/17195221
Philosophical foundation https://ciris.ai/ciris_covenant.pdf
Expand Down
4 changes: 2 additions & 2 deletions ciris_engine/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from pathlib import Path

# Version information
CIRIS_VERSION = "1.6.5.1-stable"
CIRIS_VERSION = "1.6.5.3-stable"
CIRIS_VERSION_MAJOR = 1
CIRIS_VERSION_MINOR = 6
CIRIS_VERSION_PATCH = 5
CIRIS_VERSION_BUILD = 1
CIRIS_VERSION_BUILD = 3
CIRIS_VERSION_STAGE = "stable"
CIRIS_CODENAME = "Stable Foundation" # Codename for this release

Expand Down
6 changes: 4 additions & 2 deletions ciris_engine/logic/adapters/api/routes/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from fastapi import APIRouter, Depends, HTTPException, Request, status
from pydantic import BaseModel, Field

from ciris_engine.logic.config.db_paths import get_audit_db_full_path
from ciris_engine.logic.setup.first_run import get_default_config_path, is_first_run
from ciris_engine.logic.setup.wizard import create_env_file, generate_encryption_key
from ciris_engine.schemas.api.responses import SuccessResponse
Expand Down Expand Up @@ -714,8 +715,9 @@ async def complete_setup(setup: SetupCompleteRequest, request: Request) -> Succe
detail="Runtime not available - cannot complete setup",
)

# Get audit database path from runtime's essential config
auth_db_path = str(runtime.essential_config.database.audit_db)
# Get audit database path using same resolution as AuthenticationService
# This handles both SQLite and PostgreSQL (adds _auth suffix to database name)
auth_db_path = get_audit_db_full_path(runtime.essential_config)
logger.info(f"Using runtime audit database: {auth_db_path}")

# Create users immediately (don't wait for restart)
Expand Down
17 changes: 3 additions & 14 deletions ciris_engine/logic/adapters/api/routes/wa.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import logging
import uuid
from datetime import datetime, timedelta, timezone
from datetime import datetime, timezone
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException, Query, Request
Expand Down Expand Up @@ -60,21 +60,10 @@ async def get_deferrals(
wa_service: WiseAuthorityServiceProtocol = request.app.state.wise_authority_service

try:
# Get pending deferrals
# Get pending deferrals (UI fields now included in PendingDeferral schema)
deferrals = await wa_service.get_pending_deferrals(wa_id=wa_id)

# Transform PendingDeferral to include question from reason for UI compatibility
# The TypeScript SDK expects a 'question' field
transformed_deferrals = []
for d in deferrals:
# Create a dict representation and add the question field
deferral_dict = d.model_dump()
deferral_dict["question"] = d.reason # Use reason as the question
deferral_dict["context"] = {} # Add empty context for compatibility
deferral_dict["timeout_at"] = (d.created_at + timedelta(days=7)).isoformat() # Default 7 day timeout
transformed_deferrals.append(deferral_dict)

response = DeferralListResponse(deferrals=transformed_deferrals, total=len(transformed_deferrals))
response = DeferralListResponse(deferrals=deferrals, total=len(deferrals))
Comment on lines +63 to +66
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pending deferral UI fields still empty

GET /v1/wa/deferrals now returns the raw PendingDeferral objects without the transformation that populated question, context, and timeout_at, but get_pending_deferrals constructs each PendingDeferral without setting those new fields (logic/services/governance/wise_authority/service.py lines 387-401). As a result the response still contains null/empty values for the UI-specific fields, so the WA dashboard will continue to miss the question/timeout details this fix targets.

Useful? React with 👍 / 👎.


return SuccessResponse(
data=response,
Expand Down
24 changes: 22 additions & 2 deletions ciris_engine/logic/services/governance/wise_authority/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@
logger.error(f"Failed to send deferral: {e}")
raise

async def get_pending_deferrals(self, wa_id: Optional[str] = None) -> List[PendingDeferral]:

Check failure on line 329 in ciris_engine/logic/services/governance/wise_authority/service.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 24 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=CIRISAI_CIRISAgent&issues=AZq8FenHS-tBS-2vPqAH&open=AZq8FenHS-tBS-2vPqAH&pullRequest=517
"""Get pending deferrals from the tasks table."""
result = []

Expand Down Expand Up @@ -384,10 +384,26 @@
else:
priority_str = "low"

# Create PendingDeferral
# Create PendingDeferral with UI-compatible fields
created_at_dt = datetime.fromisoformat(updated_at.replace(" ", "T")) if updated_at else self._now()
timeout_dt = created_at_dt + timedelta(days=7)

# Build rich context for UI from available deferral data
deferral_context = deferral_info.get("context", {})
ui_context: Dict[str, str] = {
"task_description": description[:500] if description else "",
}
# Add deferral-specific context fields (converted to strings for UI)
for key, value in deferral_context.items():
if value is not None:
ui_context[key] = str(value)[:200] # Limit string length
# Include original message if available
if deferral_info.get("original_message"):
ui_context["original_message"] = str(deferral_info["original_message"])[:500]

deferral = PendingDeferral(
deferral_id=deferral_id,
created_at=datetime.fromisoformat(updated_at.replace(" ", "T")) if updated_at else self._now(),
created_at=created_at_dt,
deferred_by="ciris_agent",
task_id=task_id,
thought_id=thought_id,
Expand All @@ -398,6 +414,10 @@
assigned_wa_id=None, # Not assigned in current implementation
requires_role=None, # Not specified in current implementation
status="pending",
# UI compatibility fields
question=reason, # Use reason as the question for UI display
context=ui_context, # Rich context from task and deferral data
timeout_at=timeout_dt.isoformat(), # Default 7 day timeout
)

result.append(deferral)
Expand Down
27 changes: 24 additions & 3 deletions ciris_engine/schemas/config/essential.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,33 @@
from pydantic import BaseModel, ConfigDict, Field


def _get_default_main_db() -> Path:
"""Get default main database path using central path resolution."""
from ciris_engine.logic.utils.path_resolution import get_data_dir

return get_data_dir() / "ciris_engine.db"


def _get_default_secrets_db() -> Path:
"""Get default secrets database path using central path resolution."""
from ciris_engine.logic.utils.path_resolution import get_data_dir

return get_data_dir() / "secrets.db"


def _get_default_audit_db() -> Path:
"""Get default audit database path using central path resolution."""
from ciris_engine.logic.utils.path_resolution import get_data_dir

return get_data_dir() / "ciris_audit.db"


class DatabaseConfig(BaseModel):
"""Core database paths configuration."""

main_db: Path = Field(Path("data/ciris_engine.db"), description="Main SQLite database for persistence")
secrets_db: Path = Field(Path("data/secrets.db"), description="Encrypted secrets storage database")
audit_db: Path = Field(Path("data/ciris_audit.db"), description="Audit trail database with signatures")
main_db: Path = Field(default_factory=_get_default_main_db, description="Main SQLite database for persistence")
secrets_db: Path = Field(default_factory=_get_default_secrets_db, description="Encrypted secrets storage database")
audit_db: Path = Field(default_factory=_get_default_audit_db, description="Audit trail database with signatures")
database_url: Optional[str] = Field(
None,
description="Database connection string. If set, overrides main_db path. "
Expand Down
7 changes: 7 additions & 0 deletions ciris_engine/schemas/services/authority/wise_authority.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

from pydantic import BaseModel, Field

from ciris_engine.schemas.types import JSONDict


class PermissionEntry(BaseModel):
"""A single permission entry for a WA."""
Expand Down Expand Up @@ -86,6 +88,11 @@ class PendingDeferral(BaseModel):
resolution: Optional[str] = Field(None, description="Resolution if completed")
resolved_at: Optional[datetime] = Field(None, description="Resolution time")

# UI compatibility fields
question: Optional[str] = Field(None, description="Question/description for UI display")
context: JSONDict = Field(default_factory=dict, description="Additional context for UI")
timeout_at: Optional[str] = Field(None, description="When deferral times out (ISO format)")


class DeferralResolution(BaseModel):
"""Resolution of a deferral by WA."""
Expand Down
8 changes: 8 additions & 0 deletions tests/adapters/api/test_setup_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def client_with_runtime(client, tmp_path):
mock_config = MagicMock()
mock_db_config = MagicMock()
mock_db_config.audit_db = tmp_path / "audit.db"
mock_db_config.database_url = None # SQLite mode (no PostgreSQL URL)
mock_config.database = mock_db_config
mock_runtime.essential_config = mock_config

Expand Down Expand Up @@ -781,6 +782,13 @@ def test_complete_setup_triggers_resume(
# Mock runtime with resume method and set it on app.state
mock_runtime = Mock()
mock_runtime.resume_from_first_run = AsyncMock()
# Mock essential_config for get_audit_db_full_path()
mock_config = Mock()
mock_db_config = Mock()
mock_db_config.audit_db = tmp_path / "audit.db"
mock_db_config.database_url = None # SQLite mode
mock_config.database = mock_db_config
mock_runtime.essential_config = mock_config
client.app.state.runtime = mock_runtime

response = client.post(
Expand Down
Loading