You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ClientToolSpec.name must now match ^client_[a-z][a-z0-9_]*$. Names without the client_ prefix are rejected at deserialization, so the API returns 400 before persisting any session state. This also applies to @client_tool-decorated functions, since the decorator defaults the tool name to fn.__name__. Either rename the function (e.g. def client_store_fact) or pass name="client_..." explicitly.
AgentSessionGoalRecord now requires a message_sequence_num: int field. The field is the session-wide message-sequence index of the USER-role message that declared the goal — clients use it to render goal chips adjacent to the turn they were attached to. External consumers that construct the record directly (test fixtures, custom middleware, recorded API responses) will fail validation on upgrade until they pass the new field. In-tree consumers and SDK-side to_agent_goal() re-hydration from server responses are unaffected, since the server populates the field on every record it emits.
Features Added
Sessions (experimental): Represent an activity — a drone flight, a vehicle drive, a robot arm test run — as a Session: an operational time window over zero, one, or many Devices. Create one with Session.create(name=..., device_ids=...), or use the anchored-convenience constructors Device.create_session(name=...) and Dataset.create_session(name=..., ...). Include files with Session.add_file / add_files (each optionally narrowed to a sub-window via range_min_timestamp_ns / range_max_timestamp_ns), and attach to additional devices via Session.attach_to_device(device_id). Each operation has a symmetric counterpart: removing files, detaching devices, renaming, and deleting. The Session's min_timestamp_ns / max_timestamp_ns (Unix-epoch nanoseconds) are recomputed on every file add or remove. Enumerate Sessions with Session.for_dataset, Session.for_org, Dataset.get_sessions(), or Device.list_sessions(). Session.list_topics() iterates every topic reachable through the Session's files, turning "all signals from this flight / drive / run" into a single query. Because a Session is bounded by the activity rather than the recordings, it may span many files or cover only part of one.
Content-addressable topic schemas (experimental): A Topic's field structure is now represented as a TopicSchema identified by a checksum over its fields. Topics whose fields share the same names, paths, and data types reference the same schema within an organization. Retrieve a topic's schema with Topic.get_schema(), or look one up directly via TopicSchema.for_topic(topic_id) / TopicSchema.from_id(schema_id). Lays the groundwork for finding every topic in an org with a given shape, and for comparing topics across files, sessions, and devices.
Timeline offsets on files (experimental):File.set_timeline_offset(unix_epoch_offset_ns, ...) — and its batched counterpart File.set_timeline_offsets([...]) — project a file's stored partition timestamps onto Unix-epoch wall-clock (session_time_ns = stored_time_ns + unix_epoch_offset_ns) without re-ingest. An offset can be narrowed by topic (topic / topic_name) and by timeline source (timeline_source / timeline_source_name). TimelineSourceKind names where a timestamp comes from: a field in the message (schema_field, e.g. header.stamp), the recorder-assigned log time (message_log_time), or the publisher-assigned publish time (message_publish_time). The same API is available on Topic (Topic.set_timeline_offset / set_timeline_offsets), auto-scoped to a single topic. A bag that starts at zero, a sensor that logs in device-uptime, or a camera that lags the IMU by a few milliseconds can be reconciled after ingest. Session aggregate bounds read from these projected timestamps.
AgentSession.submit_feedback(message_sequence_num, sentiment, categories=[...], notes=None) persists structured feedback on a specific assistant message. sentiment is FeedbackSentiment.POSITIVE or NEGATIVE; categories is a list of FeedbackCategory values (multi-select) that must match the selected sentiment. FeedbackCategory.OTHER requires notes. Resubmitting from the same user on the same message overwrites sentiment, categories, and notes on the existing row rather than creating a duplicate. Replaces the previous write-only Slack-only feedback path with a durable, queryable record.
AgentSession.fork(message_sequence_num) forks a session at a given message into a new session owned by the caller. Available to the session's creator for their own sessions and to Roboto admins for any session. The new session carries the source's org_id; lineage is tracked on AgentSessionRecord.forked_from_session_id and AgentSessionRecord.forked_from_message_sequence_num.
Added FeedbackSentiment, FeedbackCategory, SubmitFeedbackRequest, and AdminUpdateFeedbackRequest models to roboto.ai.agent_session for programmatic access to AI chat feedback. (AgentFeedbackRecord, the admin-triage shape, is intentionally not part of the public surface.)
AgentSessionRecord now exposes forked_from_session_id and forked_from_message_sequence_num fields. Both are None for sessions that were not created via AgentSession.fork.
Topic.get_data and Topic.get_data_as_df accept an optional representation_selector (RepresentationSelector) to choose among multiple representations. Defaults to raw, untransformed data; filter on content_format or transformations to match a specific encoding or transformation pipeline.
Topic.set_default_representation and Topic.add_message_path_representation accept optional format and transformations to describe a representation.
RepresentationRecord exposes a new format field.
New AnalysisScope in roboto.ai.core captures a time window (start_time / end_time, nanoseconds since the Unix epoch) that scopes which data the agent's tools may consider. AgentSession.start(), .send(), and .send_text() accept an analysis_scope= kwarg; on start it attaches the scope to the session, on send/send_text an explicit value replaces the session's current scope (omitting the kwarg leaves it untouched). The scope is persisted on the session and delivered to every tool invocation on the server side. The analyze_topic tool honors the scope today by clamping topic data to the window; other tools will opt in as they adopt.
New RobotoFeatureNotAvailableException in roboto.exceptions, raised when an API call targets a route gated by a feature flag that is not enabled for the caller.
AgentSession can now declare hard, verifiable goals per turn. New goals= parameter on AgentSession.start(), .send(), and .send_text() accepts a list of typed goal models from the closed roboto.ai.goals.AgentGoal union (capped at 5 entries per turn). The agent runner enforces achievement: it re-prompts up to 3 times if the LLM ends a turn without satisfying every declared goal, then reports the new AgentSessionStatus.GOALS_FAILED terminal status (which AgentSession.run() raises as a typed RobotoAgentGoalsFailedException). The message/messages parameter becomes optional when goals= is provided. Authorization runs at goal-registration time so the caller gets a fast 401 before any model resources are spent. Two concrete goals ship in this release: DatasetSummaryAgentGoal(dataset_id, summary_format_spec_prompt) directs the agent to investigate a dataset and persist a summary via SummaryService.set_dataset_summary; DatasetTriageGoal(dataset_id, label_vocabulary={label: description, ...}) directs the agent to deliberate over each label in the vocabulary and apply the ones that fit (zero or more) as dataset tags. AgentSessionRecord.goals exposes the per-goal status (PENDING / ACHIEVED / FAILED) and AgentSessionGoalRecord.to_agent_goal() re-hydrates the typed goal model from any read. Each AgentSessionGoalRecord also carries a message_sequence_num: int identifying the USER-role message that declared the goal, so clients can render goal chips adjacent to the turn they were attached to. The corresponding AgentSessionDelta now exposes a goals field so streaming consumers see goal-status transitions land alongside message and status deltas.
RobotoLLMContext is deprecated and renamed to ClientViewingContext (parameter client_context on AgentSession.start(), .send(), and .send_text(), replacing the previous context= parameter that took a RobotoLLMContext). The rename disambiguates the SDK call site now that goals and analysis_scope are also turn-level inputs — "context" was overloaded to mean both the goal/scope envelope and the client-side viewing state. The old name is still importable from roboto.ai.core and roboto.ai.agent_session for one release as a re-export of ClientViewingContext; the wire-format client_context field also accepts the legacy context JSON key for one release via validation_alias=AliasChoices("client_context", "context"). Both compatibility aliases will be removed in a subsequent release — migrate to from roboto.ai.core import ClientViewingContext and pass it via client_context=.
Collections now support event resources. CollectionResourceType.Event works with Collection.create(resource_type=CollectionResourceType.Event, event_ids=[...]), Collection.from_id(...), and the Collection.events, add_event(), and remove_event() helpers. The CLI now accepts --event-id for roboto collections create and --add-event-id / --remove-event-id for roboto collections update <collection_id>.
Behavior Changes
BYOB cross-org access now returns RobotoNotFoundException (404) instead of RobotoUnauthorizedException (401). With the integrations table now keyed on (org_id, bucket_name), looking up a bucket registered only under another org no longer surfaces that registration's existence; it surfaces as not-found, matching the security posture of the rest of the org-scoped API. Affects Dataset.get_single_bucket_creds, File.import_one, File.import_batch, File.delete, File.get_signed_upload_url, and File.get_signed_url. Callers that switch on exception type (rather than surfacing the message) should add RobotoNotFoundException to their cross-org branch.
Bugs Fixed
get_data and get_data_as_df now return full-resolution image data for topics ingested by the updated ros_ingestion action; previously they returned a downsampled, re-encoded version.