Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d440c8e
docs(mobile): approvals and steering plan (grounded round-trip + phas…
ardaerzin Jul 27, 2026
92e4e42
docs(mobile): record approvals-plan decisions and the two standing fo…
ardaerzin Jul 27, 2026
fbf6ab9
feat(runner): widen the approval warm-park window to 30 minutes
ardaerzin Jul 27, 2026
395a88c
feat(mobile): M0.1 project liveness poll + running badge on session rows
ardaerzin Jul 27, 2026
9545595
feat(mobile): M0.2 project-wide pending-approval badges on the sessio…
ardaerzin Jul 27, 2026
d6f93c5
feat(api): compose the approval answer for the detached interactions …
ardaerzin Jul 27, 2026
be2078e
feat(mobile): M0.3 approval-pending card + tightened records poll in …
ardaerzin Jul 27, 2026
c6bab59
feat(chat): M1.1 lite references-only agent resume request builder
ardaerzin Jul 27, 2026
3e57e2e
feat(chat): M1.2 resolve an agent invoke URL from workflow references
ardaerzin Jul 27, 2026
3c2939a
feat(mobile): M1.3 approve/deny pending approvals from the phone
ardaerzin Jul 27, 2026
6dcf0d9
feat(mobile): M1.4 stop a running session (cooperative cancel)
ardaerzin Jul 27, 2026
35ff82b
fix(mobile): review fixes for the approval resume path
ardaerzin Jul 27, 2026
f6663ba
docs(mobile): record the flows-lite, auth-lite, and approvals execution
ardaerzin Jul 27, 2026
536e3a9
fix(mobile): pin the chat and list headers and contain scrolling
ardaerzin Jul 27, 2026
f31f4ee
fix(mobile): keep the transcript pinned to the latest message
ardaerzin Jul 27, 2026
5a18fbd
fix(mobile): safe-area, input-zoom, and tap-target mechanics
ardaerzin Jul 27, 2026
b4d0728
docs(mobile): M3 live-relay plan (change-notification SSE)
ardaerzin Jul 27, 2026
1735855
docs(mobile): record M3 decisions (paragraph-level SSE + lifecycle ev…
ardaerzin Jul 27, 2026
74f8bfa
fix(mobile): bubble the transcript text and collapse thoughts by default
ardaerzin Jul 27, 2026
ea261e8
fix(sessions): name the tool in a replayed approval envelope
ardaerzin Aug 2, 2026
17237d5
fix(sessions): read the gated call through the typed interaction request
ardaerzin Aug 2, 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