Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5654bf2
docs(mobile): approvals and steering plan (grounded round-trip + phas…
ardaerzin Jul 27, 2026
0f9ee07
docs(mobile): record approvals-plan decisions and the two standing fo…
ardaerzin Jul 27, 2026
344f4a6
feat(runner): widen the approval warm-park window to 30 minutes
ardaerzin Jul 27, 2026
75e0523
feat(mobile): M0.1 project liveness poll + running badge on session rows
ardaerzin Jul 27, 2026
d6d2622
feat(mobile): M0.2 project-wide pending-approval badges on the sessio…
ardaerzin Jul 27, 2026
995ddbc
feat(api): compose the approval answer for the detached interactions …
ardaerzin Jul 27, 2026
b3c2b1e
feat(mobile): M0.3 approval-pending card + tightened records poll in …
ardaerzin Jul 27, 2026
9ea3b5c
feat(chat): M1.1 lite references-only agent resume request builder
ardaerzin Jul 27, 2026
2b36a47
feat(chat): M1.2 resolve an agent invoke URL from workflow references
ardaerzin Jul 27, 2026
2c37a5e
feat(mobile): M1.3 approve/deny pending approvals from the phone
ardaerzin Jul 27, 2026
30c86a8
feat(mobile): M1.4 stop a running session (cooperative cancel)
ardaerzin Jul 27, 2026
4f492f4
fix(mobile): review fixes for the approval resume path
ardaerzin Jul 27, 2026
4646f97
docs(mobile): record the flows-lite, auth-lite, and approvals execution
ardaerzin Jul 27, 2026
a7e7ea4
fix(mobile): pin the chat and list headers and contain scrolling
ardaerzin Jul 27, 2026
a3d62b7
fix(mobile): keep the transcript pinned to the latest message
ardaerzin Jul 27, 2026
bf2e3f3
fix(mobile): safe-area, input-zoom, and tap-target mechanics
ardaerzin Jul 27, 2026
7665363
docs(mobile): M3 live-relay plan (change-notification SSE)
ardaerzin Jul 27, 2026
c468db7
docs(mobile): record M3 decisions (paragraph-level SSE + lifecycle ev…
ardaerzin Jul 27, 2026
c5fe844
fix(mobile): bubble the transcript text and collapse thoughts by default
ardaerzin Jul 27, 2026
07d778f
fix(sessions): name the tool in a replayed approval envelope
ardaerzin Aug 2, 2026
07d8c25
fix(sessions): read the gated call through the typed interaction request
ardaerzin Aug 2, 2026
788f428
fix(api): replay a user turn's attachments, and anchor on the stored …
ardaerzin Aug 5, 2026
25286a6
merge release/v0.111.0 into feat/mobile-approvals
mmabrouk Aug 7, 2026
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
2 changes: 2 additions & 0 deletions api/entrypoints/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,7 @@ async def _dispatch_detached_run(*, project_id, user_id, request) -> str:
_interactions_dispatcher = InteractionsDispatcher(
workflows_service=workflows_service,
interactions_service=interactions_service,
records_service=records_service,
dispatch_fn=_dispatch_detached_run,
)

Expand Down Expand Up @@ -1098,6 +1099,7 @@ async def _dispatch_detached_run(*, project_id, user_id, request) -> str:
turns_service=session_turns_service,
sessions_service=sessions_service,
respond_task=_interactions_worker.respond_interaction,
interactions_dispatcher=_interactions_dispatcher,
)

# PLATFORM ADMIN ---------------------------------------------------------------
Expand Down
13 changes: 12 additions & 1 deletion api/entrypoints/worker_queues.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from oss.src.core.evaluators.service import EvaluatorsService, SimpleEvaluatorsService
from oss.src.core.queries.service import QueriesService
from oss.src.core.sessions.interactions.service import SessionInteractionsService
from oss.src.core.sessions.records.service import RecordsService
from oss.src.core.testcases.service import TestcasesService
from oss.src.core.testsets.service import SimpleTestsetsService, TestsetsService
from oss.src.core.tracing.service import TracingService
Expand All @@ -67,7 +68,11 @@
QueryVariantDBE,
)
from oss.src.dbs.postgres.sessions.interactions.dao import SessionInteractionsDAO
from oss.src.dbs.postgres.shared.engine import get_transactions_engine
from oss.src.dbs.postgres.sessions.records.dao import RecordsDAO
from oss.src.dbs.postgres.shared.engine import (
get_analytics_engine,
get_transactions_engine,
)
from oss.src.dbs.postgres.testcases.dbes import TestcaseBlobDBE
from oss.src.dbs.postgres.testsets.dbes import (
TestsetArtifactDBE,
Expand Down Expand Up @@ -214,6 +219,11 @@ def _build_interactions_broker() -> tuple[AsyncBroker, int]:
environments_service.embeds_service = embeds_service

interactions_service = SessionInteractionsService(interactions_dao=interactions_dao)
# Approval answers replay the session's durable records into the resume conversation;
# records live on the analytics engine (same as the API composition in routers.py).
records_service = RecordsService(
records_dao=RecordsDAO(engine=get_analytics_engine()),
)

async def _dispatch_detached_run(*, project_id, user_id, request) -> str:
result = await workflows_service.invoke_workflow_detached(
Expand All @@ -226,6 +236,7 @@ async def _dispatch_detached_run(*, project_id, user_id, request) -> str:
interactions_dispatcher = InteractionsDispatcher(
workflows_service=workflows_service,
interactions_service=interactions_service,
records_service=records_service,
dispatch_fn=_dispatch_detached_run,
)
InteractionsWorker(broker=broker, dispatcher=interactions_dispatcher)
Expand Down
3 changes: 3 additions & 0 deletions api/oss/src/apis/fastapi/sessions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ class SessionInteractionsResponse(BaseModel):


class SessionInteractionRespondRequest(BaseModel):
# For a user_approval interaction the answer is {approved: bool, tool_call_id?: str,
# message?: str} — the dispatcher composes the full resume conversation server-side
# (interactions_dispatcher.compose_approval_messages). Other kinds pass through as-is.
answer: Optional[Dict[str, Any]] = None


Expand Down
19 changes: 17 additions & 2 deletions api/oss/src/apis/fastapi/sessions/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -639,10 +639,15 @@ def __init__(
interactions_service: SessionInteractionsService,
workflows_service: WorkflowsService,
respond_task: Optional[Any] = None,
# InteractionsDispatcher (typed loosely, like respond_task: the API layer does not
# import the tasks layer). When present, the no-worker respond fallback goes through
# it so both paths share ONE answer-composition implementation.
interactions_dispatcher: Optional[Any] = None,
) -> None:
self.interactions_service = interactions_service
self.workflows_service = workflows_service
self.respond_task = respond_task
self.interactions_dispatcher = interactions_dispatcher

self.router = APIRouter()

Expand Down Expand Up @@ -910,15 +915,23 @@ async def respond_interaction(
detail="Interaction is no longer pending",
)

# Enqueue onto the interactions worker when wired; otherwise fall back to an
# inline blocking invoke (keeps the route usable in minimal/test compositions).
# Enqueue onto the interactions worker when wired; otherwise fall back to the
# dispatcher directly (same answer composition, fired in-process), or as a last
# resort an inline blocking invoke (keeps minimal/test compositions usable).
if self.respond_task is not None:
await self.respond_task.kiq(
project_id=str(project_id),
user_id=str(user_id),
interaction_id=str(interaction_id),
answer=answer,
)
elif self.interactions_dispatcher is not None:
await self.interactions_dispatcher.respond(
project_id=UUID(str(project_id)),
user_id=UUID(str(user_id)),
interaction_id=interaction_id,
answer=answer,
)
else:
references = (
{
Expand Down Expand Up @@ -1672,6 +1685,7 @@ def __init__(
turns_service: SessionTurnsService,
sessions_service: SessionsService,
respond_task: Optional[Any] = None,
interactions_dispatcher: Optional[Any] = None,
) -> None:
self.streams = SessionStreamsRouter(
service=streams_service,
Expand All @@ -1682,6 +1696,7 @@ def __init__(
interactions_service=interactions_service,
workflows_service=workflows_service,
respond_task=respond_task,
interactions_dispatcher=interactions_dispatcher,
)
self.attachments = SessionAttachmentsRouter(
attachments_service=attachments_service,
Expand Down
Loading
Loading