Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
510b994
feat(mounts): add agent_id column, index, and backfill
jp-agenta Jul 17, 2026
4bcfb62
feat(mounts): populate and query agent_id
jp-agenta Jul 17, 2026
9e07c12
test(mounts): cover agent_id population, query filter, and backfill p…
jp-agenta Jul 17, 2026
b092771
feat(tracing): add session/user/agent columns to spans
jp-agenta Jul 17, 2026
4015a8a
feat(tracing): populate session/user/agent columns root-only at ingest
jp-agenta Jul 17, 2026
1fabb13
feat(sdk): add store_agent and wire it into NormalizerMiddleware
jp-agenta Jul 17, 2026
00c77cb
feat(otlp): add ag.agent.id to receiver adapters
jp-agenta Jul 17, 2026
b22a9cc
feat(api): add session_turns migration (core_oss)
jp-agenta Jul 17, 2026
b7f4878
feat(api): add session_turns domain (dtos, service, dao)
jp-agenta Jul 17, 2026
3c7bffb
feat(api): add turns endpoints + wire into sessions router
jp-agenta Jul 17, 2026
8801f2c
test(api): add SessionTurnsDAO integration tests
jp-agenta Jul 17, 2026
6e05641
feat(api): tag session_records with turn_id + span_id
jp-agenta Jul 17, 2026
cf03d46
feat(runner): thread turn_id + span_id into record ingest
jp-agenta Jul 17, 2026
491b039
feat(api): merge session_states identity into session_streams (WP2)
jp-agenta Jul 17, 2026
fe6d837
test(api): cover the stream-header merge and rewrite stale states tests
jp-agenta Jul 17, 2026
9fc84e3
feat(runner): capture stream_id for free from the session heartbeat
jp-agenta Jul 17, 2026
37abe4f
feat(runner): append session_turns rows instead of the states RMW
jp-agenta Jul 17, 2026
22688f0
feat(api): add session-scoped hard-delete/unarchive DAO plumbing (WP5)
jp-agenta Jul 17, 2026
e40de87
feat(api): add SessionsService + root /sessions router (WP5)
jp-agenta Jul 17, 2026
2e4a6bf
test(api): cover WP5 root sessions ops — service, router, DAO fan-out
jp-agenta Jul 17, 2026
ae43f73
feat(api): rename streams command discriminator prompt -> inputs/data…
jp-agenta Jul 17, 2026
60d6599
feat(api): wire kill to the runner's /kill for sandbox teardown (W7.3)
jp-agenta Jul 17, 2026
b508ad7
feat(runner): abort the in-flight run when a heartbeat reports interr…
jp-agenta Jul 17, 2026
8bea828
merge: WP6 mounts agent_id into sessions-extensions
jp-agenta Jul 17, 2026
53eb69d
fix(migrations): re-parent mounts agent_id to core_oss 016 (after tur…
jp-agenta Jul 17, 2026
10e0f82
merge: WP0 telemetry span identity columns + ag.agent.id
jp-agenta Jul 17, 2026
bb8e730
merge: WP4 records turn_id/span_id tags
jp-agenta Jul 17, 2026
126f64b
fix(migrations): re-parent records turn/span to tracing_oss 004 (afte…
jp-agenta Jul 17, 2026
3378f24
merge: WP3 runner turn-append cutover
jp-agenta Jul 17, 2026
62ab94a
docs(sessions): sandbox_id now on the latest turn row, not session_st…
jp-agenta Jul 17, 2026
74dcddf
Merge branch 'big-agents-add-sessions' into feat/sessions-extensions
jp-agenta Jul 17, 2026
4f51a23
chore(runner): bump OTel to 2.x + daytona 0.198 (PR #5362) for green …
jp-agenta Jul 17, 2026
594ca03
regen fern clients
jp-agenta Jul 17, 2026
d06474c
fix some runner tests
jp-agenta Jul 17, 2026
2e78e8c
quick test fixes
jp-agenta Jul 17, 2026
5e79598
fix more runner/api tests
jp-agenta Jul 17, 2026
5f1a41c
clean up types
jp-agenta Jul 17, 2026
0707f41
clean up types and migrations
jp-agenta Jul 17, 2026
1532ec7
sdk/runner fixes
jp-agenta Jul 17, 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
39 changes: 27 additions & 12 deletions api/entrypoints/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,6 @@
from oss.src.core.ai_services.service import AIServicesService
from oss.src.apis.fastapi.ai_services.router import AIServicesRouter

from oss.src.dbs.postgres.sessions.states.dbes import SessionStateDBE # noqa: F401
from oss.src.dbs.postgres.sessions.states.dao import SessionStatesDAO
from oss.src.core.sessions.states.service import SessionStatesService

from oss.src.core.accounts.service import PlatformAdminAccountsService
from oss.src.apis.fastapi.accounts.router import PlatformAdminAccountsRouter
from oss.src.dbs.postgres.gateway.connections.dao import ConnectionsDAO
Expand Down Expand Up @@ -178,10 +174,15 @@
from oss.src.tasks.asyncio.sessions.orphan_sweep import orphan_sweep_loop
from oss.src.dbs.redis.shared.engine import get_lock_engine

from oss.src.dbs.postgres.sessions.turns.dbes import SessionTurnDBE # noqa: F401
from oss.src.dbs.postgres.sessions.turns.dao import SessionTurnsDAO
from oss.src.core.sessions.turns.service import SessionTurnsService

# Interactions
from oss.src.dbs.postgres.sessions.interactions.dbes import SessionInteractionDBE # noqa: F401
from oss.src.dbs.postgres.sessions.interactions.dao import SessionInteractionsDAO
from oss.src.core.sessions.interactions.service import SessionInteractionsService
from oss.src.core.sessions.service import SessionsService
from oss.src.tasks.asyncio.sessions.interactions_dispatcher import (
InteractionsDispatcher,
)
Expand Down Expand Up @@ -549,6 +550,7 @@ async def lifespan(*args, **kwargs):
evaluations_dao = EvaluationsDAO(engine=_transactions_engine)
folders_dao = FoldersDAO(engine=_transactions_engine)
session_streams_dao = SessionStreamsDAO(engine=_transactions_engine)
session_turns_dao = SessionTurnsDAO(engine=_transactions_engine)

connections_dao = ConnectionsDAO(engine=_transactions_engine)
mounts_dao = MountsDAO(engine=_transactions_engine)
Expand Down Expand Up @@ -616,6 +618,10 @@ async def lifespan(*args, **kwargs):
lock_engine=_lock_engine,
)

session_turns_service = SessionTurnsService(
turns_dao=session_turns_dao,
)

workflows_service = WorkflowsService(
workflows_dao=workflows_dao,
static_catalog=StaticWorkflowCatalog(),
Expand Down Expand Up @@ -1026,22 +1032,26 @@ async def _dispatch_detached_run(*, project_id, user_id, request) -> str:
ai_services_service=ai_services_service,
)

# SESSION STATES ---------------------------------------------------------------
# SESSIONS ---------------------------------------------------------------------
# Session header rename (name/description) lives on the streams router
# (PUT /sessions/streams/header) via streams_service.set_header.

session_states_dao = SessionStatesDAO(engine=_transactions_engine)

session_states_service = SessionStatesService(
session_states_dao=session_states_dao,
sessions_service = SessionsService(
streams_service=session_streams_service,
turns_service=session_turns_service,
interactions_service=interactions_service,
mounts_service=mounts_service,
)

sessions = SessionsRouter(
streams_service=session_streams_service,
states_service=session_states_service,
records_service=records_service,
interactions_service=interactions_service,
workflows_service=workflows_service,
session_mounts_service=session_mounts_service,
mounts_service=mounts_service,
turns_service=session_turns_service,
sessions_service=sessions_service,
respond_task=_interactions_worker.respond_interaction,
)

Expand Down Expand Up @@ -1479,15 +1489,20 @@ async def _dispatch_detached_run(*, project_id, user_id, request) -> str:
tags=["Sessions"],
)

app.include_router(
router=sessions.turns.router,
prefix="/sessions/turns",
tags=["Sessions"],
)

app.include_router(
router=platform_admin_accounts.router,
prefix="/admin",
tags=["Admin"],
)

app.include_router(
router=sessions.states.router,
prefix="/sessions",
router=sessions.root.router,
tags=["Sessions"],
)

Expand Down
1 change: 0 additions & 1 deletion api/oss/databases/postgres/migrations/core/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import oss.src.dbs.postgres.users.dbes # noqa: F401
import oss.src.dbs.postgres.workflows.dbes # noqa: F401
import oss.src.dbs.postgres.webhooks.dbes # noqa: F401
import oss.src.dbs.postgres.sessions.states.dbes # noqa: F401


# this is the Alembic Config object, which provides
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"""add session_turns table

Revision ID: oss000000014
Revises: oss000000013
Create Date: 2026-07-17 00:00:00.000000

"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

revision: str = "oss000000014"
down_revision: Union[str, None] = "oss000000013"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.create_table(
"session_turns",
sa.Column("id", sa.UUID(as_uuid=True), nullable=False),
sa.Column("project_id", sa.UUID(as_uuid=True), nullable=False),
sa.Column("session_id", sa.String(), nullable=False),
sa.Column("stream_id", sa.UUID(as_uuid=True), nullable=False),
sa.Column("turn_index", sa.Integer(), nullable=False),
sa.Column("harness_kind", sa.String(), nullable=False),
sa.Column("agent_session_id", sa.String(), nullable=True),
sa.Column("sandbox_id", sa.String(), nullable=True),
sa.Column(
"references",
postgresql.JSONB(none_as_null=True),
nullable=True,
),
sa.Column("trace_id", sa.UUID(as_uuid=True), nullable=True),
sa.Column("span_id", sa.UUID(as_uuid=True), nullable=True),
sa.Column("start_time", sa.TIMESTAMP(timezone=True), nullable=True),
sa.Column("end_time", sa.TIMESTAMP(timezone=True), nullable=True),
sa.Column(
"created_at",
sa.TIMESTAMP(timezone=True),
server_default=sa.func.current_timestamp(),
nullable=True,
),
sa.Column("updated_at", sa.TIMESTAMP(timezone=True), nullable=True),
sa.Column("deleted_at", sa.TIMESTAMP(timezone=True), nullable=True),
sa.Column("created_by_id", sa.UUID(as_uuid=True), nullable=True),
sa.Column("updated_by_id", sa.UUID(as_uuid=True), nullable=True),
sa.Column("deleted_by_id", sa.UUID(as_uuid=True), nullable=True),
sa.ForeignKeyConstraint(
["project_id"],
["projects.id"],
ondelete="CASCADE",
),
sa.ForeignKeyConstraint(
["project_id", "stream_id"],
["session_streams.project_id", "session_streams.id"],
ondelete="NO ACTION",
),
sa.PrimaryKeyConstraint("project_id", "id"),
)
op.create_index(
"ix_session_turns_project_id_session_id",
"session_turns",
["project_id", "session_id"],
)
op.create_index(
"ix_session_turns_project_id_session_id_turn_index",
"session_turns",
["project_id", "session_id", "turn_index"],
)
op.create_index(
"ix_session_turns_references",
"session_turns",
["references"],
postgresql_using="gin",
postgresql_ops={"references": "jsonb_path_ops"},
)


def downgrade() -> None:
op.drop_index(
"ix_session_turns_references",
table_name="session_turns",
)
op.drop_index(
"ix_session_turns_project_id_session_id_turn_index",
table_name="session_turns",
)
op.drop_index(
"ix_session_turns_project_id_session_id",
table_name="session_turns",
)
op.drop_table("session_turns")
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""add name/description header to session_streams

Revision ID: oss000000015
Revises: oss000000014
Create Date: 2026-07-17 00:00:00.000000

"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa

revision: str = "oss000000015"
down_revision: Union[str, None] = "oss000000014"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.add_column(
"session_streams",
sa.Column("name", sa.String(), nullable=True),
)
op.add_column(
"session_streams",
sa.Column("description", sa.String(), nullable=True),
)


def downgrade() -> None:
op.drop_column("session_streams", "description")
op.drop_column("session_streams", "name")
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""add mounts agent_id column, index, and backfill

Mirrors session_id: a bare, nullable varchar populated only for agent mounts
(session mounts stay agent_id-null), with a partial index for querying "mounts
for agent X" the same way session_id already supports "mounts for session X".

Backfill: mounts are core DB, so a data migration is allowed here (unlike the
tracing DB). Agent-mount slugs are minted as
`__ag__agent__<canonical_artifact_id>__<name>` (`mint_agent_slug`,
`core/mounts/service.py`) — the artifact id is the raw canonical (lowercase)
UUID, not a hash, so it is deterministically recoverable straight out of the
slug. Session-mount slugs (`__ag__session__<uuid5(session_id)>__<name>`) hash
the session id, so no session_id backfill is possible or attempted here.

Revision ID: oss000000016
Revises: oss000000015
Create Date: 2026-07-17 00:00:00.000000

"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa

revision: str = "oss000000016"
down_revision: Union[str, None] = "oss000000015"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

_AGENT_SLUG_PREFIX = "__ag__agent__"


def upgrade() -> None:
op.add_column("mounts", sa.Column("agent_id", sa.String(), nullable=True))

op.create_index(
"ix_mounts_project_id_agent_id",
"mounts",
["project_id", "agent_id"],
unique=False,
postgresql_where=sa.text("agent_id IS NOT NULL"),
)

conn = op.get_bind()
conn.execute(
sa.text(
f"""
UPDATE mounts
SET agent_id = split_part(substr(slug, {len(_AGENT_SLUG_PREFIX) + 1}), '__', 1)
WHERE left(slug, {len(_AGENT_SLUG_PREFIX)}) = '{_AGENT_SLUG_PREFIX}'
"""
)
)


def downgrade() -> None:
op.drop_index("ix_mounts_project_id_agent_id", table_name="mounts")
op.drop_column("mounts", "agent_id")
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""drop session_states table

Superseded by the session_streams header (name/description) — the /sessions/states/
router now reads/writes the merged stream row via streams_service. The standalone
table, its DAO/service, and DBE are orphaned; drop the physical table.

Revision ID: oss000000017
Revises: oss000000016
"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql


revision: str = "oss000000017"
down_revision: Union[str, None] = "oss000000016"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.drop_table("session_states")


def downgrade() -> None:
op.create_table(
"session_states",
sa.Column("project_id", sa.UUID(), nullable=False),
sa.Column("id", sa.UUID(), nullable=False),
sa.Column("session_id", sa.String(), nullable=False),
sa.Column("sandbox_id", sa.String(), nullable=True),
sa.Column(
"data",
postgresql.JSON(astext_type=sa.Text()),
nullable=True,
),
sa.Column("flags", postgresql.JSONB(none_as_null=True), nullable=True),
sa.Column("tags", postgresql.JSONB(none_as_null=True), nullable=True),
sa.Column("meta", postgresql.JSON(none_as_null=True), nullable=True),
sa.Column(
"created_at",
sa.TIMESTAMP(timezone=True),
server_default=sa.text("CURRENT_TIMESTAMP"),
nullable=True,
),
sa.Column("updated_at", sa.TIMESTAMP(timezone=True), nullable=True),
sa.Column("deleted_at", sa.TIMESTAMP(timezone=True), nullable=True),
sa.Column("created_by_id", sa.UUID(), nullable=True),
sa.Column("updated_by_id", sa.UUID(), nullable=True),
sa.Column("deleted_by_id", sa.UUID(), nullable=True),
sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("project_id", "id"),
sa.UniqueConstraint(
"project_id",
"session_id",
name="uq_session_states_project_session_id",
),
)
Loading
Loading