ellipsis-dev 0.44.0__tar.gz → 0.45.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ellipsis-dev
3
- Version: 0.44.0
3
+ Version: 0.45.0
4
4
  Summary: Python SDK for the Ellipsis agents platform: every /v1 operation, sync and async.
5
5
  Project-URL: Homepage, https://www.ellipsis.dev
6
6
  Project-URL: Documentation, https://www.ellipsis.dev/docs/api
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ellipsis-dev"
7
- version = "0.44.0"
7
+ version = "0.45.0"
8
8
  description = "Python SDK for the Ellipsis agents platform: every /v1 operation, sync and async."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1809,64 +1809,6 @@ class _SyncSessions(SyncSessionsSugar):
1809
1809
  def __init__(self, transport: Transport) -> None:
1810
1810
  self._transport = transport
1811
1811
 
1812
- def analytics(
1813
- self,
1814
- id: str,
1815
- ) -> models.SessionAnalytics:
1816
- """Get Session Analytics
1817
-
1818
- Measured tokens and spend over time, plus estimated content cost.
1819
-
1820
- Token lanes use the request's saved rates. Content attribution
1821
- apportions input/cache spend by text size, counting carried context
1822
- on every request. It is an estimate, not a tool call fee.
1823
- Only the latest 200 retained request bodies are inspected; missing
1824
- or unsupported context remains explicitly unattributed. No raw
1825
- prompts or tool results are returned. Session tokens may read only
1826
- their own session's analytics.
1827
-
1828
- Required permissions: read:sessions.
1829
- API key: Supported.
1830
- CLI user token: Supported.
1831
- Session token: Requires the ellipsis:api scope and the required grants in the token's permissions.ellipsis configuration. Only the token's own session.
1832
- Higher permission levels include lower levels: read < write < delete.
1833
- Grant match patterns can further restrict access to individual resources.
1834
- """
1835
- path = f"/v1/sessions/{_quote(id)}/analytics"
1836
- response = self._transport.request(
1837
- "GET", path, response_model=models.SessionAnalytics
1838
- )
1839
- assert response is not None
1840
- return response
1841
-
1842
- def diff(
1843
- self,
1844
- id: str,
1845
- ) -> models.SessionDiffResponse:
1846
- """Get Session Diff
1847
-
1848
- Return the session's uncommitted changes as a patch.
1849
-
1850
- One unified-diff section per changed file, captured from the
1851
- environment's working tree when the session ended. The per-file
1852
- counts ride on the session itself (`git.repos[].files`); this
1853
- is the text behind them. Empty when the tree was clean. A
1854
- session token may read only its own session's diff.
1855
-
1856
- Required permissions: read:sessions.
1857
- API key: Supported.
1858
- CLI user token: Supported.
1859
- Session token: Requires the ellipsis:api scope and the required grants in the token's permissions.ellipsis configuration. Only the token's own session.
1860
- Higher permission levels include lower levels: read < write < delete.
1861
- Grant match patterns can further restrict access to individual resources.
1862
- """
1863
- path = f"/v1/sessions/{_quote(id)}/diff"
1864
- response = self._transport.request(
1865
- "GET", path, response_model=models.SessionDiffResponse
1866
- )
1867
- assert response is not None
1868
- return response
1869
-
1870
1812
  def download(
1871
1813
  self,
1872
1814
  id: str,
@@ -1927,31 +1869,6 @@ class _SyncSessions(SyncSessionsSugar):
1927
1869
  assert response is not None
1928
1870
  return response
1929
1871
 
1930
- def git(
1931
- self,
1932
- id: str,
1933
- ) -> models.SessionGitResponse:
1934
- """Get Session Git
1935
-
1936
- The session's GitHub-side git view: its commits, and each PR it
1937
- opened joined to the live gh_prs row and reviews. The identity list
1938
- (output_git) rides the session itself over the WebSocket; this is
1939
- the join a "run" notify tells an open pane to refetch.
1940
-
1941
- Required permissions: read:sessions.
1942
- API key: Supported.
1943
- CLI user token: Supported.
1944
- Session token: Requires the ellipsis:api scope and the required grants in the token's permissions.ellipsis configuration.
1945
- Higher permission levels include lower levels: read < write < delete.
1946
- Grant match patterns can further restrict access to individual resources.
1947
- """
1948
- path = f"/v1/sessions/{_quote(id)}/git"
1949
- response = self._transport.request(
1950
- "GET", path, response_model=models.SessionGitResponse
1951
- )
1952
- assert response is not None
1953
- return response
1954
-
1955
1872
  def image(
1956
1873
  self,
1957
1874
  session_id: str,
@@ -2072,42 +1989,6 @@ class _SyncSessions(SyncSessionsSugar):
2072
1989
  assert response is not None
2073
1990
  return response
2074
1991
 
2075
- def proxy(
2076
- self,
2077
- id: str,
2078
- *,
2079
- limit: Union[int, None] = None,
2080
- cursor: Union[str, None] = None,
2081
- ) -> SyncPage[models.ProxyRequest, models.SessionProxyResponse]:
2082
- """Get Session Proxy
2083
-
2084
- The requests the session's environment sent to GitHub through
2085
- Ellipsis, newest first. Each is one `proxy_requests` row as stored.
2086
-
2087
- Required permissions: read:sessions.
2088
- API key: Supported.
2089
- CLI user token: Supported.
2090
- Session token: Requires the ellipsis:api scope and the required grants in the token's permissions.ellipsis configuration. Only the token's own session.
2091
- Higher permission levels include lower levels: read < write < delete.
2092
- Grant match patterns can further restrict access to individual resources.
2093
- """
2094
- path = f"/v1/sessions/{_quote(id)}/proxy"
2095
- query = build_query(
2096
- {
2097
- "limit": limit,
2098
- "cursor": cursor,
2099
- }
2100
- )
2101
- response = self._transport.request(
2102
- "GET", path, response_model=models.SessionProxyResponse, query=query
2103
- )
2104
- assert response is not None
2105
- return SyncPage(
2106
- response,
2107
- "proxy_requests",
2108
- lambda _cursor: self.proxy(id=id, limit=limit, cursor=_cursor),
2109
- )
2110
-
2111
1992
  def records(
2112
1993
  self,
2113
1994
  id: str,
@@ -2325,6 +2206,35 @@ class _SyncSessions(SyncSessionsSugar):
2325
2206
  assert response is not None
2326
2207
  return response
2327
2208
 
2209
+ def timeline(
2210
+ self,
2211
+ id: str,
2212
+ ) -> models.SessionTimelineResponse:
2213
+ """Get Session Timeline
2214
+
2215
+ What the session did, newest first. Each time its environment
2216
+ started preparing, became ready, fell asleep after its idle window,
2217
+ and woke for the next message, its close, and what it did on
2218
+ GitHub through Ellipsis, such as the repositories it cloned, its pushes,
2219
+ what it did to pull requests and issues, the files it committed
2220
+ through the API, releases, and workflow runs, read from the
2221
+ requests its environment sent. A run of the same GitHub action is
2222
+ one entry.
2223
+
2224
+ Required permissions: read:sessions.
2225
+ API key: Supported.
2226
+ CLI user token: Supported.
2227
+ Session token: Requires the ellipsis:api scope and the required grants in the token's permissions.ellipsis configuration. Only the token's own session.
2228
+ Higher permission levels include lower levels: read < write < delete.
2229
+ Grant match patterns can further restrict access to individual resources.
2230
+ """
2231
+ path = f"/v1/sessions/{_quote(id)}/timeline"
2232
+ response = self._transport.request(
2233
+ "GET", path, response_model=models.SessionTimelineResponse
2234
+ )
2235
+ assert response is not None
2236
+ return response
2237
+
2328
2238
  def update(
2329
2239
  self,
2330
2240
  id: str,
@@ -4793,64 +4703,6 @@ class _AsyncSessions(AsyncSessionsSugar):
4793
4703
  def __init__(self, transport: AsyncTransport) -> None:
4794
4704
  self._transport = transport
4795
4705
 
4796
- async def analytics(
4797
- self,
4798
- id: str,
4799
- ) -> models.SessionAnalytics:
4800
- """Get Session Analytics
4801
-
4802
- Measured tokens and spend over time, plus estimated content cost.
4803
-
4804
- Token lanes use the request's saved rates. Content attribution
4805
- apportions input/cache spend by text size, counting carried context
4806
- on every request. It is an estimate, not a tool call fee.
4807
- Only the latest 200 retained request bodies are inspected; missing
4808
- or unsupported context remains explicitly unattributed. No raw
4809
- prompts or tool results are returned. Session tokens may read only
4810
- their own session's analytics.
4811
-
4812
- Required permissions: read:sessions.
4813
- API key: Supported.
4814
- CLI user token: Supported.
4815
- Session token: Requires the ellipsis:api scope and the required grants in the token's permissions.ellipsis configuration. Only the token's own session.
4816
- Higher permission levels include lower levels: read < write < delete.
4817
- Grant match patterns can further restrict access to individual resources.
4818
- """
4819
- path = f"/v1/sessions/{_quote(id)}/analytics"
4820
- response = await self._transport.request(
4821
- "GET", path, response_model=models.SessionAnalytics
4822
- )
4823
- assert response is not None
4824
- return response
4825
-
4826
- async def diff(
4827
- self,
4828
- id: str,
4829
- ) -> models.SessionDiffResponse:
4830
- """Get Session Diff
4831
-
4832
- Return the session's uncommitted changes as a patch.
4833
-
4834
- One unified-diff section per changed file, captured from the
4835
- environment's working tree when the session ended. The per-file
4836
- counts ride on the session itself (`git.repos[].files`); this
4837
- is the text behind them. Empty when the tree was clean. A
4838
- session token may read only its own session's diff.
4839
-
4840
- Required permissions: read:sessions.
4841
- API key: Supported.
4842
- CLI user token: Supported.
4843
- Session token: Requires the ellipsis:api scope and the required grants in the token's permissions.ellipsis configuration. Only the token's own session.
4844
- Higher permission levels include lower levels: read < write < delete.
4845
- Grant match patterns can further restrict access to individual resources.
4846
- """
4847
- path = f"/v1/sessions/{_quote(id)}/diff"
4848
- response = await self._transport.request(
4849
- "GET", path, response_model=models.SessionDiffResponse
4850
- )
4851
- assert response is not None
4852
- return response
4853
-
4854
4706
  async def download(
4855
4707
  self,
4856
4708
  id: str,
@@ -4911,31 +4763,6 @@ class _AsyncSessions(AsyncSessionsSugar):
4911
4763
  assert response is not None
4912
4764
  return response
4913
4765
 
4914
- async def git(
4915
- self,
4916
- id: str,
4917
- ) -> models.SessionGitResponse:
4918
- """Get Session Git
4919
-
4920
- The session's GitHub-side git view: its commits, and each PR it
4921
- opened joined to the live gh_prs row and reviews. The identity list
4922
- (output_git) rides the session itself over the WebSocket; this is
4923
- the join a "run" notify tells an open pane to refetch.
4924
-
4925
- Required permissions: read:sessions.
4926
- API key: Supported.
4927
- CLI user token: Supported.
4928
- Session token: Requires the ellipsis:api scope and the required grants in the token's permissions.ellipsis configuration.
4929
- Higher permission levels include lower levels: read < write < delete.
4930
- Grant match patterns can further restrict access to individual resources.
4931
- """
4932
- path = f"/v1/sessions/{_quote(id)}/git"
4933
- response = await self._transport.request(
4934
- "GET", path, response_model=models.SessionGitResponse
4935
- )
4936
- assert response is not None
4937
- return response
4938
-
4939
4766
  async def image(
4940
4767
  self,
4941
4768
  session_id: str,
@@ -5056,42 +4883,6 @@ class _AsyncSessions(AsyncSessionsSugar):
5056
4883
  assert response is not None
5057
4884
  return response
5058
4885
 
5059
- async def proxy(
5060
- self,
5061
- id: str,
5062
- *,
5063
- limit: Union[int, None] = None,
5064
- cursor: Union[str, None] = None,
5065
- ) -> AsyncPage[models.ProxyRequest, models.SessionProxyResponse]:
5066
- """Get Session Proxy
5067
-
5068
- The requests the session's environment sent to GitHub through
5069
- Ellipsis, newest first. Each is one `proxy_requests` row as stored.
5070
-
5071
- Required permissions: read:sessions.
5072
- API key: Supported.
5073
- CLI user token: Supported.
5074
- Session token: Requires the ellipsis:api scope and the required grants in the token's permissions.ellipsis configuration. Only the token's own session.
5075
- Higher permission levels include lower levels: read < write < delete.
5076
- Grant match patterns can further restrict access to individual resources.
5077
- """
5078
- path = f"/v1/sessions/{_quote(id)}/proxy"
5079
- query = build_query(
5080
- {
5081
- "limit": limit,
5082
- "cursor": cursor,
5083
- }
5084
- )
5085
- response = await self._transport.request(
5086
- "GET", path, response_model=models.SessionProxyResponse, query=query
5087
- )
5088
- assert response is not None
5089
- return AsyncPage(
5090
- response,
5091
- "proxy_requests",
5092
- lambda _cursor: self.proxy(id=id, limit=limit, cursor=_cursor),
5093
- )
5094
-
5095
4886
  async def records(
5096
4887
  self,
5097
4888
  id: str,
@@ -5309,6 +5100,35 @@ class _AsyncSessions(AsyncSessionsSugar):
5309
5100
  assert response is not None
5310
5101
  return response
5311
5102
 
5103
+ async def timeline(
5104
+ self,
5105
+ id: str,
5106
+ ) -> models.SessionTimelineResponse:
5107
+ """Get Session Timeline
5108
+
5109
+ What the session did, newest first. Each time its environment
5110
+ started preparing, became ready, fell asleep after its idle window,
5111
+ and woke for the next message, its close, and what it did on
5112
+ GitHub through Ellipsis, such as the repositories it cloned, its pushes,
5113
+ what it did to pull requests and issues, the files it committed
5114
+ through the API, releases, and workflow runs, read from the
5115
+ requests its environment sent. A run of the same GitHub action is
5116
+ one entry.
5117
+
5118
+ Required permissions: read:sessions.
5119
+ API key: Supported.
5120
+ CLI user token: Supported.
5121
+ Session token: Requires the ellipsis:api scope and the required grants in the token's permissions.ellipsis configuration. Only the token's own session.
5122
+ Higher permission levels include lower levels: read < write < delete.
5123
+ Grant match patterns can further restrict access to individual resources.
5124
+ """
5125
+ path = f"/v1/sessions/{_quote(id)}/timeline"
5126
+ response = await self._transport.request(
5127
+ "GET", path, response_model=models.SessionTimelineResponse
5128
+ )
5129
+ assert response is not None
5130
+ return response
5131
+
5312
5132
  async def update(
5313
5133
  self,
5314
5134
  id: str,
@@ -117,6 +117,7 @@ def parse_frame(data: str | bytes) -> frames.StreamFrame | None:
117
117
 
118
118
  _FRAME_TYPES: dict[Any, Any] = {
119
119
  "session": frames.SessionFrame,
120
+ "timeline": frames.TimelineFrame,
120
121
  "records": frames.RecordsFrame,
121
122
  "delta": frames.DeltaFrame,
122
123
  "heartbeat": frames.HeartbeatFrame,
@@ -75,12 +75,37 @@ class SessionFrame(EllipsisModel):
75
75
  type: Literal["session"]
76
76
 
77
77
 
78
+ class TimelineFrame(EllipsisModel):
79
+ """The session's whole timeline, newest first: sent after the first session
80
+ frame, and resent whole whenever it changes. The same events
81
+ `GET /v1/sessions/{id}/timeline` returns.
82
+ """
83
+
84
+ events: list[
85
+ Union[
86
+ models.EnvironmentTimelineEvent,
87
+ models.SessionClosedTimelineEvent,
88
+ models.GithubCloneTimelineEvent,
89
+ models.GithubPushTimelineEvent,
90
+ models.GithubCommitTimelineEvent,
91
+ models.GithubPullRequestTimelineEvent,
92
+ models.GithubIssueTimelineEvent,
93
+ models.GithubReleaseTimelineEvent,
94
+ models.GithubWorkflowTimelineEvent,
95
+ models.GithubRefusedTimelineEvent,
96
+ models.GithubOtherTimelineEvent,
97
+ ]
98
+ ] = Field(description="What the session did, newest first.")
99
+ type: Literal["timeline"]
100
+
101
+
78
102
  StreamFrame = Union[
79
103
  DeltaFrame,
80
104
  ErrorFrame,
81
105
  HeartbeatFrame,
82
106
  RecordsFrame,
83
107
  SessionFrame,
108
+ TimelineFrame,
84
109
  ]
85
110
  """One server->client frame. Unknown frame types must be ignored
86
111
  (additive server changes are not a protocol break)."""
@@ -92,5 +117,6 @@ __all__ = [
92
117
  "HeartbeatFrame",
93
118
  "RecordsFrame",
94
119
  "SessionFrame",
120
+ "TimelineFrame",
95
121
  "StreamFrame",
96
122
  ]
@@ -71,7 +71,6 @@ CustomerAuditLogEventType = Literal[
71
71
  "refund_issued",
72
72
  "auto_recharge_updated",
73
73
  "platform_fee_updated",
74
- "github_proxy_updated",
75
74
  ]
76
75
 
77
76
 
@@ -3484,6 +3483,16 @@ Not derivable from the row: `environment_id` says WHICH environment, not
3484
3483
  how it was chosen, and is null for the built-in basic environment."""
3485
3484
 
3486
3485
 
3486
+ class EnvironmentTimelineEvent(EllipsisModel):
3487
+ """The session's environment started preparing, became ready, was paused
3488
+ after its idle window, or was resumed for the next message.
3489
+ """
3490
+
3491
+ action: Literal["started", "ready", "asleep", "awake"]
3492
+ at: datetime
3493
+ type: Literal["environment"]
3494
+
3495
+
3487
3496
  class EnvironmentsListResponse(EllipsisModel):
3488
3497
  """The caller's saved environments."""
3489
3498
 
@@ -3889,6 +3898,30 @@ class GithubCheckSuiteEvent(EllipsisModel):
3889
3898
  url: str
3890
3899
 
3891
3900
 
3901
+ class GithubCloneTimelineEvent(EllipsisModel):
3902
+ """A repository cloned into the environment."""
3903
+
3904
+ at: datetime = Field(description="When the last request behind it ended.")
3905
+ ok: bool = Field(description="Whether GitHub did it.")
3906
+ repository: str
3907
+ type: Literal["github.clone"]
3908
+
3909
+
3910
+ class GithubCommitTimelineEvent(EllipsisModel):
3911
+ """Files committed through the API, one entry for each run of commits to
3912
+ the same branch.
3913
+ """
3914
+
3915
+ at: datetime = Field(description="When the last request behind it ended.")
3916
+ branch: Union[str, None] = Field(
3917
+ description="Null when the repository's default branch."
3918
+ )
3919
+ ok: bool = Field(description="Whether GitHub did it.")
3920
+ paths: list[str] = Field(description="The files written or deleted.")
3921
+ repository: str
3922
+ type: Literal["github.commit"]
3923
+
3924
+
3892
3925
  class GithubConfigEllipsis(EllipsisModel):
3893
3926
  """The product namespace of a GitHub routing file, with an explicit kind."""
3894
3927
 
@@ -4001,6 +4034,19 @@ class GithubIssueEvent(EllipsisModel):
4001
4034
  url: str
4002
4035
 
4003
4036
 
4037
+ class GithubIssueTimelineEvent(EllipsisModel):
4038
+ action: Literal["opened", "edited", "commented", "closed", "reopened"]
4039
+ at: datetime = Field(description="When the last request behind it ended.")
4040
+ number: Union[int, None] = Field(
4041
+ description="Null when Ellipsis could not tell which."
4042
+ )
4043
+ ok: bool = Field(description="Whether GitHub did it.")
4044
+ repository: Union[str, None] = Field(
4045
+ description="Null when Ellipsis could not tell which."
4046
+ )
4047
+ type: Literal["github.issue"]
4048
+
4049
+
4004
4050
  class LinkedSlackIdentity(EllipsisModel):
4005
4051
  """The Slack identity linked to a GitHub member."""
4006
4052
 
@@ -4048,6 +4094,19 @@ class GithubMembersListResponse(EllipsisModel):
4048
4094
  )
4049
4095
 
4050
4096
 
4097
+ class GithubOtherTimelineEvent(EllipsisModel):
4098
+ """A change sent to GitHub that none of the other types describe."""
4099
+
4100
+ at: datetime = Field(description="When the last request behind it ended.")
4101
+ method: str
4102
+ ok: bool = Field(description="Whether GitHub did it.")
4103
+ operation: Union[str, None] = Field(
4104
+ description="The GraphQL mutation, for `/api/graphql`."
4105
+ )
4106
+ path: str
4107
+ type: Literal["github.other"]
4108
+
4109
+
4051
4110
  class GithubPullRequestEvent(EllipsisModel):
4052
4111
  action: Union[PullRequestAction, Literal["review_commented"]]
4053
4112
  actor: Union[SessionEventActor, None]
@@ -4059,6 +4118,38 @@ class GithubPullRequestEvent(EllipsisModel):
4059
4118
  url: str
4060
4119
 
4061
4120
 
4121
+ class GithubPullRequestTimelineEvent(EllipsisModel):
4122
+ action: Literal[
4123
+ "opened",
4124
+ "edited",
4125
+ "ready_for_review",
4126
+ "converted_to_draft",
4127
+ "review_requested",
4128
+ "commented",
4129
+ "reviewed",
4130
+ "review_commented",
4131
+ "thread_resolved",
4132
+ "branch_updated",
4133
+ "auto_merge_enabled",
4134
+ "merge_queued",
4135
+ "merged",
4136
+ "closed",
4137
+ "reopened",
4138
+ ]
4139
+ at: datetime = Field(description="When the last request behind it ended.")
4140
+ number: Union[int, None] = Field(
4141
+ description="Null when Ellipsis could not tell which."
4142
+ )
4143
+ ok: bool = Field(description="Whether GitHub did it.")
4144
+ repository: Union[str, None] = Field(
4145
+ description="Null when Ellipsis could not tell which."
4146
+ )
4147
+ review: Union[Literal["approved", "changes_requested", "commented"], None] = Field(
4148
+ description="The review's verdict, for `reviewed`."
4149
+ )
4150
+ type: Literal["github.pull_request"]
4151
+
4152
+
4062
4153
  class GithubPushEvent(EllipsisModel):
4063
4154
  actor: Union[SessionEventActor, None]
4064
4155
  after: str
@@ -4069,6 +4160,31 @@ class GithubPushEvent(EllipsisModel):
4069
4160
  url: str
4070
4161
 
4071
4162
 
4163
+ class GithubPushTimelineEvent(EllipsisModel):
4164
+ """A branch or tag pushed, created, or deleted, over git or through the API."""
4165
+
4166
+ action: Literal["pushed", "created", "deleted"]
4167
+ at: datetime = Field(description="When the last request behind it ended.")
4168
+ branch: Union[str, None] = Field(
4169
+ description="Null for a tag, or when Ellipsis did not record the push's branches."
4170
+ )
4171
+ ok: bool = Field(description="Whether GitHub did it.")
4172
+ repository: str
4173
+ tag: Union[str, None] = Field(description="Null for a branch.")
4174
+ type: Literal["github.push"]
4175
+
4176
+
4177
+ class GithubRefusedTimelineEvent(EllipsisModel):
4178
+ """Requests Ellipsis refused before they reached GitHub, one entry for
4179
+ each run of them.
4180
+ """
4181
+
4182
+ at: datetime = Field(description="When the last request behind it ended.")
4183
+ ok: bool = Field(description="Whether GitHub did it.")
4184
+ reason: str = Field(description="The `reason` on the refused requests.")
4185
+ type: Literal["github.refused"]
4186
+
4187
+
4072
4188
  class GithubReleaseEvent(EllipsisModel):
4073
4189
  action: ReleaseAction
4074
4190
  actor: Union[SessionEventActor, None]
@@ -4080,6 +4196,15 @@ class GithubReleaseEvent(EllipsisModel):
4080
4196
  url: str
4081
4197
 
4082
4198
 
4199
+ class GithubReleaseTimelineEvent(EllipsisModel):
4200
+ action: Literal["created", "edited", "deleted"]
4201
+ at: datetime = Field(description="When the last request behind it ended.")
4202
+ ok: bool = Field(description="Whether GitHub did it.")
4203
+ repository: str
4204
+ tag: Union[str, None]
4205
+ type: Literal["github.release"]
4206
+
4207
+
4083
4208
  class GithubRepository(EllipsisModel):
4084
4209
  """A repository connected to the account's GitHub installation — a valid
4085
4210
  target for `repository` when creating an agent and for the
@@ -4210,6 +4335,20 @@ class GithubWorkflowRunEvent(EllipsisModel):
4210
4335
  url: str
4211
4336
 
4212
4337
 
4338
+ class GithubWorkflowTimelineEvent(EllipsisModel):
4339
+ """A GitHub Actions workflow run, or its logs read."""
4340
+
4341
+ action: Literal["dispatched", "rerun", "cancelled", "logs_read"]
4342
+ at: datetime = Field(description="When the last request behind it ended.")
4343
+ ok: bool = Field(description="Whether GitHub did it.")
4344
+ repository: str
4345
+ run_id: Union[int, None]
4346
+ type: Literal["github.workflow"]
4347
+ workflow: Union[str, None] = Field(
4348
+ description="The workflow file or id, for `dispatched`."
4349
+ )
4350
+
4351
+
4213
4352
  class ValidationError(EllipsisModel):
4214
4353
  ctx: Union[dict[str, Any], None] = None
4215
4354
  input: Union[Any, None] = None
@@ -4942,42 +5081,6 @@ PromptBlockedReason = Literal[
4942
5081
  """Why direct prompting is refused. Absent when prompting is allowed."""
4943
5082
 
4944
5083
 
4945
- class ProxyRequest(EllipsisModel):
4946
- """One request an environment sent through Ellipsis to GitHub, as the
4947
- proxy recorded it.
4948
- """
4949
-
4950
- connection: str = Field(description="Where it went. `github` today.")
4951
- created_at: datetime = Field(description="When the request ended.")
4952
- customer_id: Union[str, None] = Field(default=None, description="The account.")
4953
- duration_ms: int = Field(description="Milliseconds from arrival to the end.")
4954
- id: str
4955
- method: str = Field(description="The HTTP method as the environment sent it.")
4956
- path: str = Field(description="The path as the environment sent it.")
4957
- reason: Union[str, None] = Field(
4958
- default=None,
4959
- description="Why Ellipsis answered itself or the transfer ended early, null when GitHub answered in full.",
4960
- )
4961
- request_bytes: Union[int, None] = Field(
4962
- default=None, description="Body bytes sent, null when none was read."
4963
- )
4964
- response_bytes: Union[int, None] = Field(
4965
- default=None,
4966
- description="Body bytes received, null when nothing was forwarded.",
4967
- )
4968
- sandbox_id: Union[str, None] = Field(
4969
- default=None,
4970
- description="The environment that sent it, the id `GET /v1/vms/{id}` reads.",
4971
- )
4972
- status_code: Union[int, None] = Field(
4973
- default=None, description="The status the environment received."
4974
- )
4975
- upstream_host: Union[str, None] = Field(
4976
- default=None,
4977
- description="The host Ellipsis forwarded it to, null when Ellipsis answered itself.",
4978
- )
4979
-
4980
-
4981
5084
  class PullRequestBranch(EllipsisModel):
4982
5085
  """One side of a pull request, a branch and the commit it pointed at."""
4983
5086
 
@@ -4989,23 +5092,23 @@ class PullRequest(EllipsisModel):
4989
5092
  """A GitHub pull request. A session's pull requests are read live. A review's is kept as it was when the review ran."""
4990
5093
 
4991
5094
  additions: Union[int, None] = Field(
4992
- default=None, description="Lines added, null until GitHub reports them."
5095
+ description="Lines added, null until GitHub reports them."
4993
5096
  )
4994
5097
  author: GithubAccountSnippet = Field(description="Who opened the pull request.")
4995
5098
  base: PullRequestBranch = Field(description="The branch the changes merge into.")
4996
5099
  changed_files: Union[int, None] = Field(
4997
- default=None, description="Files changed, null until GitHub reports them."
5100
+ description="Files changed, null until GitHub reports them."
4998
5101
  )
4999
5102
  commits: Union[int, None] = Field(
5000
- default=None, description="Commits, null until GitHub reports them."
5103
+ description="Commits, null until GitHub reports them."
5001
5104
  )
5002
5105
  created_at: datetime
5003
5106
  deletions: Union[int, None] = Field(
5004
- default=None, description="Lines deleted, null until GitHub reports them."
5107
+ description="Lines deleted, null until GitHub reports them."
5005
5108
  )
5006
5109
  head: PullRequestBranch = Field(description="The branch with the changes.")
5007
5110
  id: int = Field(description="The pull request's GitHub id.")
5008
- merged_at: Union[datetime, None] = None
5111
+ merged_at: Union[datetime, None]
5009
5112
  number: int
5010
5113
  repository: GithubRepositorySnippet = Field(
5011
5114
  description="The repository the pull request merges into."
@@ -5391,6 +5494,48 @@ class SessionEnvironment(EllipsisModel):
5391
5494
  variables: list[EnvironmentVariable_Output]
5392
5495
 
5393
5496
 
5497
+ class SessionHandler(EllipsisModel):
5498
+ """The saved handler and its display name when the session started."""
5499
+
5500
+ agent_name: str = Field(
5501
+ description="The handler's effective display name when the session started: ellipsis.name, or the service default (Slack, GitHub, Linear, or sentry)."
5502
+ )
5503
+ id: str = Field(description="Identifier of the saved handler.")
5504
+ service: HandlerService = Field(description="The service the handler responds to.")
5505
+ sha: str = Field(
5506
+ description="Content fingerprint of the validated handler configuration used when the session started. This is not the Git commit SHA."
5507
+ )
5508
+
5509
+
5510
+ class SessionOutput(EllipsisModel):
5511
+ """Structured output on the wire: the contract and, when the agent has
5512
+ submitted one, the payload. `result` is None while the session runs and
5513
+ when the agent submitted nothing; it is the agent's own JSON, in the
5514
+ shape of `json_schema`.
5515
+ """
5516
+
5517
+ json_schema: dict[str, Any] = Field(
5518
+ description="JSON Schema the agent's result must satisfy."
5519
+ )
5520
+ result: Union[dict[str, Any], None] = Field(
5521
+ description="The payload the agent submitted, or null until it submits one."
5522
+ )
5523
+
5524
+
5525
+ class SessionParent(EllipsisModel):
5526
+ """The predecessor session this session continues."""
5527
+
5528
+ session_id: Union[str, None] = Field(
5529
+ description="Identifier of the predecessor session this session continues, if any."
5530
+ )
5531
+
5532
+
5533
+ class SessionReview(EllipsisModel):
5534
+ """The code review a reviewer session ran for."""
5535
+
5536
+ id: str = Field(description="Identifier of the code review.")
5537
+
5538
+
5394
5539
  class SessionGitCommit(EllipsisModel):
5395
5540
  """One commit the session created in this repo (everything past the
5396
5541
  provisioning checkout's sha), captured from the environment rather than the
@@ -5411,6 +5556,9 @@ class SessionGitFile(EllipsisModel):
5411
5556
 
5412
5557
  additions: int
5413
5558
  deletions: int
5559
+ patch: Union[str, None] = Field(
5560
+ description="The file's unified diff, `diff --git` header included, exactly as git emitted it. Null when the session's diff passed 100 KB and this file was left out."
5561
+ )
5414
5562
  path: str
5415
5563
  status: str
5416
5564
 
@@ -5425,6 +5573,9 @@ class SessionGitPr(EllipsisModel):
5425
5573
 
5426
5574
  gh_pr_id: Union[int, None]
5427
5575
  number: int
5576
+ pull_request: Union[PullRequest, None] = Field(
5577
+ description="The pull request as GitHub last reported it. Null until Ellipsis has read it from GitHub."
5578
+ )
5428
5579
  title: Union[str, None]
5429
5580
  url: str
5430
5581
 
@@ -5452,48 +5603,14 @@ class SessionGit(EllipsisModel):
5452
5603
  repos: list[SessionGitRepo]
5453
5604
 
5454
5605
 
5455
- class SessionHandler(EllipsisModel):
5456
- """The saved handler and its display name when the session started."""
5606
+ class SessionScm(EllipsisModel):
5607
+ """What the session did in source control, by provider."""
5457
5608
 
5458
- agent_name: str = Field(
5459
- description="The handler's effective display name when the session started: ellipsis.name, or the service default (Slack, GitHub, Linear, or sentry)."
5460
- )
5461
- id: str = Field(description="Identifier of the saved handler.")
5462
- service: HandlerService = Field(description="The service the handler responds to.")
5463
- sha: str = Field(
5464
- description="Content fingerprint of the validated handler configuration used when the session started. This is not the Git commit SHA."
5609
+ github: Union[SessionGit, None] = Field(
5610
+ description="Each GitHub repository in the session's environment and what the session did to it. Null if nothing was ever captured."
5465
5611
  )
5466
5612
 
5467
5613
 
5468
- class SessionOutput(EllipsisModel):
5469
- """Structured output on the wire: the contract and, when the agent has
5470
- submitted one, the payload. `result` is None while the session runs and
5471
- when the agent submitted nothing; it is the agent's own JSON, in the
5472
- shape of `json_schema`.
5473
- """
5474
-
5475
- json_schema: dict[str, Any] = Field(
5476
- description="JSON Schema the agent's result must satisfy."
5477
- )
5478
- result: Union[dict[str, Any], None] = Field(
5479
- description="The payload the agent submitted, or null until it submits one."
5480
- )
5481
-
5482
-
5483
- class SessionParent(EllipsisModel):
5484
- """The predecessor session this session continues."""
5485
-
5486
- session_id: Union[str, None] = Field(
5487
- description="Identifier of the predecessor session this session continues, if any."
5488
- )
5489
-
5490
-
5491
- class SessionReview(EllipsisModel):
5492
- """The code review a reviewer session ran for."""
5493
-
5494
- id: str = Field(description="Identifier of the code review.")
5495
-
5496
-
5497
5614
  SessionSource = Literal["react", "code_review", "web", "api", "cli", "mention", "cron"]
5498
5615
  """Where a session came from: the credential that started it (web app,
5499
5616
  API key, CLI) or the trigger that fired (react, mention, cron, code
@@ -5606,9 +5723,6 @@ class Session(EllipsisModel):
5606
5723
  gemini: Union[GeminiConfig_Output, None] = Field(
5607
5724
  description="Gemini CLI input and native options. Set exactly one of claude_code, codex, or gemini."
5608
5725
  )
5609
- git: Union[SessionGit, None] = Field(
5610
- description="What the session did to git, one entry per repository in its workspace: the commit and branch it sits on, per-file line counts for its uncommitted changes, and the pull requests it opened. Null if nothing was ever captured."
5611
- )
5612
5726
  handler: Union[SessionHandler, None] = Field(
5613
5727
  description="The saved handler and its effective display name when this session started, frozen at creation. Null for built-in responders, legacy sessions, and sessions started without a handler."
5614
5728
  )
@@ -5628,6 +5742,9 @@ class Session(EllipsisModel):
5628
5742
  review: Union[SessionReview, None] = Field(
5629
5743
  description="The code review this session ran for; null unless the source is `code_review`."
5630
5744
  )
5745
+ scm: SessionScm = Field(
5746
+ description="What the session did in source control, by provider. Per repository: the commit and branch it sits on, its commits, each uncommitted file with its line counts and diff, and the pull requests it opened."
5747
+ )
5631
5748
  skills: list[SkillRef_Output] = Field(
5632
5749
  description="Skills installed for this session."
5633
5750
  )
@@ -5647,116 +5764,12 @@ class Session(EllipsisModel):
5647
5764
  )
5648
5765
 
5649
5766
 
5650
- class SessionSpendItem(EllipsisModel):
5651
- charged: int = 0
5652
- cost: int = 0
5653
- key: str
5654
- label: str
5655
- tokens: int = 0
5656
-
5657
-
5658
- class SessionCostCategory(EllipsisModel):
5659
- charged: int
5660
- items: list[SessionSpendItem]
5661
- key: str
5662
- label: str
5663
-
5664
-
5665
- class SessionToolCount(EllipsisModel):
5666
- calls: int
5667
- name: str
5668
-
5669
-
5670
- class SessionUsageBucket(EllipsisModel):
5671
- cache_read_tokens: int = 0
5672
- cache_write_tokens: int = 0
5673
- charged: int = 0
5674
- date: datetime
5675
- input_tokens: int = 0
5676
- output_tokens: int = 0
5677
- requests: int = 0
5678
-
5679
-
5680
- class SessionAnalytics(EllipsisModel):
5681
- attributed_requests: int
5682
- attribution: list[SessionCostCategory] = Field(
5683
- description="Estimated input content cost by serialized text size, allocated from measured input/cache spend. Repeated context is counted on every request. Output and compute remain separate. These estimates never affect billing."
5684
- )
5685
- attribution_request_limit: int = Field(
5686
- description="Maximum recent stored request bodies inspected. Older, missing, multimodal, or incomplete context is left unattributed."
5687
- )
5688
- bucket_seconds: int
5689
- charged: int
5690
- historical_entries: int = Field(
5691
- description="Carried-forward billing totals without per-request measurements. Included in spend, excluded from the timeline and request counts."
5692
- )
5693
- pending_requests: int
5694
- requests: int
5695
- series: list[SessionUsageBucket]
5696
- spend: list[SessionSpendItem] = Field(
5697
- description="Measured token lanes priced using each request's saved rate card, plus compute. Rounding is reconciled to the ledger."
5698
- )
5699
- tools: list[SessionToolCount] = Field(
5700
- description="Tool invocations from retained session records. These counts are not tool call charges; third-party tool fees are not metered."
5701
- )
5702
- unresolved_requests: int
5703
-
5704
-
5705
- class SessionGitPatchFile(EllipsisModel):
5706
- """One file's patch, split out of `git diff HEAD` at capture time.
5767
+ class SessionClosedTimelineEvent(EllipsisModel):
5768
+ """The session closed."""
5707
5769
 
5708
- Stored per file rather than as one blob because that is how a viewer reads
5709
- it: a row per changed file, opened one at a time. Counts and status are NOT
5710
- repeated here — they live on the matching SessionGitFile in
5711
- `sessions.output_git`, which rides on every session read, so a surface
5712
- lists the changed files (and their +/−) before it fetches a single hunk. The
5713
- join key is (full_name, path).
5714
-
5715
- `patch` is the unified-diff section for this file, `diff --git` header
5716
- included, exactly as git emitted it.
5717
- """
5718
-
5719
- full_name: str
5720
- patch: str
5721
- path: str
5722
-
5723
-
5724
- class SessionDiffResponse(EllipsisModel):
5725
- """A session's uncommitted changes, one patch per file."""
5726
-
5727
- files: list[SessionGitPatchFile] = Field(
5728
- description="One unified-diff section per changed file, `diff --git` header included, exactly as git emitted it. Join to the per-file counts on `git.repos[].files` by (full_name, path). Empty when the tree was clean or no capture ran."
5729
- )
5730
- omitted_paths: list[str] = Field(
5731
- description="Paths whose hunks were dropped to fit the storage cap, so a viewer can name what is missing instead of showing a silently partial diff."
5732
- )
5733
-
5734
-
5735
- class SessionPullRequestReview(EllipsisModel):
5736
- body: Union[str, None]
5737
- html_url: str
5738
- id: int
5739
- state: str
5740
- submitted_at: datetime
5741
- user: Union[GithubAccountSnippet, None]
5742
-
5743
-
5744
- class SessionPrEntry(EllipsisModel):
5745
- number: int
5746
- pull_request: Union[PullRequest, None]
5747
- reviews: list[SessionPullRequestReview]
5748
- url: str
5749
-
5750
-
5751
- class SessionGitRepoEntry(EllipsisModel):
5752
- commits: list[SessionGitCommit]
5753
- commits_total: int
5754
- full_name: str
5755
- prs: list[SessionPrEntry]
5756
-
5757
-
5758
- class SessionGitResponse(EllipsisModel):
5759
- repos: list[SessionGitRepoEntry]
5770
+ action: Literal["closed"]
5771
+ at: datetime
5772
+ type: Literal["session"]
5760
5773
 
5761
5774
 
5762
5775
  class SessionMessageResponse(EllipsisModel):
@@ -5767,17 +5780,6 @@ class SessionMessageResponse(EllipsisModel):
5767
5780
  )
5768
5781
 
5769
5782
 
5770
- class SessionProxyResponse(EllipsisModel):
5771
- has_more: bool = Field(
5772
- default=False, description="Whether more results exist past this page."
5773
- )
5774
- next_cursor: Union[str, None] = Field(
5775
- default=None,
5776
- description="Pass as `cursor` to fetch the next page. Null on the last page. Opaque — echo it back rather than parsing it.",
5777
- )
5778
- proxy_requests: list[ProxyRequest]
5779
-
5780
-
5781
5783
  class UnknownSessionRecord(EllipsisRecord):
5782
5784
  """Unrecognized or unparseable payload; the complete original stays readable."""
5783
5785
 
@@ -5840,6 +5842,24 @@ class SessionResponse(EllipsisModel):
5840
5842
  session: Session = Field(description="The session.")
5841
5843
 
5842
5844
 
5845
+ class SessionTimelineResponse(EllipsisModel):
5846
+ events: list[
5847
+ Union[
5848
+ EnvironmentTimelineEvent,
5849
+ SessionClosedTimelineEvent,
5850
+ GithubCloneTimelineEvent,
5851
+ GithubPushTimelineEvent,
5852
+ GithubCommitTimelineEvent,
5853
+ GithubPullRequestTimelineEvent,
5854
+ GithubIssueTimelineEvent,
5855
+ GithubReleaseTimelineEvent,
5856
+ GithubWorkflowTimelineEvent,
5857
+ GithubRefusedTimelineEvent,
5858
+ GithubOtherTimelineEvent,
5859
+ ]
5860
+ ] = Field(description="Newest first.")
5861
+
5862
+
5843
5863
  class SessionsListResponse(EllipsisModel):
5844
5864
  """A page of sessions in the lean wire shape."""
5845
5865
 
@@ -5854,7 +5874,6 @@ class SessionsListResponse(EllipsisModel):
5854
5874
  default=None,
5855
5875
  description="Pass as `cursor` to fetch the next page. Null on the last page. Opaque — echo it back rather than parsing it.",
5856
5876
  )
5857
- pull_requests: Union[dict[str, PullRequest], None] = None
5858
5877
  sessions: list[Session] = Field(
5859
5878
  description="Lean wire rows — the same shape GET /v1/sessions/{id} and the stream's session frames carry. The heavy blobs (config snapshot, input/output) live on the detail endpoints."
5860
5879
  )
@@ -6496,6 +6515,7 @@ __all__ = [
6496
6515
  "EnvironmentResponse",
6497
6516
  "EnvironmentScript",
6498
6517
  "EnvironmentSource",
6518
+ "EnvironmentTimelineEvent",
6499
6519
  "EnvironmentVariable_Input",
6500
6520
  "EnvironmentVariable_Output",
6501
6521
  "EnvironmentsListResponse",
@@ -6530,21 +6550,29 @@ __all__ = [
6530
6550
  "GithubAccountType",
6531
6551
  "GithubCheckRunEvent",
6532
6552
  "GithubCheckSuiteEvent",
6553
+ "GithubCloneTimelineEvent",
6554
+ "GithubCommitTimelineEvent",
6533
6555
  "GithubConfig",
6534
6556
  "GithubConfigEllipsis",
6535
6557
  "GithubHandler",
6536
6558
  "GithubIntegrationSummary",
6537
6559
  "GithubIssueEvent",
6560
+ "GithubIssueTimelineEvent",
6538
6561
  "GithubMember",
6539
6562
  "GithubMembersListResponse",
6540
6563
  "GithubMentionAudience",
6541
6564
  "GithubMentionAuthors",
6542
6565
  "GithubMentionPolicy",
6566
+ "GithubOtherTimelineEvent",
6543
6567
  "GithubPermissions_Input",
6544
6568
  "GithubPermissions_Output",
6545
6569
  "GithubPullRequestEvent",
6570
+ "GithubPullRequestTimelineEvent",
6546
6571
  "GithubPushEvent",
6572
+ "GithubPushTimelineEvent",
6573
+ "GithubRefusedTimelineEvent",
6547
6574
  "GithubReleaseEvent",
6575
+ "GithubReleaseTimelineEvent",
6548
6576
  "GithubRepositoriesListResponse",
6549
6577
  "GithubRepository",
6550
6578
  "GithubRepositorySnippet",
@@ -6553,6 +6581,7 @@ __all__ = [
6553
6581
  "GithubStatusEvent",
6554
6582
  "GithubUser",
6555
6583
  "GithubWorkflowRunEvent",
6584
+ "GithubWorkflowTimelineEvent",
6556
6585
  "HTTPValidationError",
6557
6586
  "Handler",
6558
6587
  "HandlerMetricsResponse",
@@ -6638,7 +6667,6 @@ __all__ = [
6638
6667
  "PlatformSessionRecord",
6639
6668
  "PollCliAuthResponse",
6640
6669
  "PromptBlockedReason",
6641
- "ProxyRequest",
6642
6670
  "PublicFeeSchedule",
6643
6671
  "PullRequest",
6644
6672
  "PullRequestAction",
@@ -6720,31 +6748,26 @@ __all__ = [
6720
6748
  "SeriesInterval",
6721
6749
  "Session",
6722
6750
  "SessionAgent",
6723
- "SessionAnalytics",
6724
6751
  "SessionArchived",
6725
6752
  "SessionAttribution",
6726
6753
  "SessionBudgetDefault",
6727
6754
  "SessionCapability",
6728
6755
  "SessionClosedPayload",
6729
6756
  "SessionClosedRecord",
6757
+ "SessionClosedTimelineEvent",
6730
6758
  "SessionConfig_Input",
6731
6759
  "SessionConfig_Output",
6732
6760
  "SessionConversation",
6733
6761
  "SessionConversationConfig_Input",
6734
6762
  "SessionConversationConfig_Output",
6735
- "SessionCostCategory",
6736
- "SessionDiffResponse",
6737
6763
  "SessionEnvironment",
6738
6764
  "SessionError",
6739
6765
  "SessionEventActor",
6740
6766
  "SessionGit",
6741
6767
  "SessionGitCommit",
6742
6768
  "SessionGitFile",
6743
- "SessionGitPatchFile",
6744
6769
  "SessionGitPr",
6745
6770
  "SessionGitRepo",
6746
- "SessionGitRepoEntry",
6747
- "SessionGitResponse",
6748
6771
  "SessionHandler",
6749
6772
  "SessionLogSegment",
6750
6773
  "SessionMessage",
@@ -6752,21 +6775,17 @@ __all__ = [
6752
6775
  "SessionMessageResponse",
6753
6776
  "SessionOutput",
6754
6777
  "SessionParent",
6755
- "SessionPrEntry",
6756
6778
  "SessionPrompting",
6757
- "SessionProxyResponse",
6758
- "SessionPullRequestReview",
6759
6779
  "SessionRecord",
6760
6780
  "SessionRecordsListResponse",
6761
6781
  "SessionResponse",
6762
6782
  "SessionReview",
6783
+ "SessionScm",
6763
6784
  "SessionSource",
6764
- "SessionSpendItem",
6765
6785
  "SessionStatus",
6766
6786
  "SessionSummary",
6767
6787
  "SessionSummarySource",
6768
- "SessionToolCount",
6769
- "SessionUsageBucket",
6788
+ "SessionTimelineResponse",
6770
6789
  "SessionsListResponse",
6771
6790
  "SettingsRef_Input",
6772
6791
  "SettingsRef_Output",
File without changes
File without changes