sinatra-dev 0.1.0__py3-none-any.whl

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.
Files changed (50) hide show
  1. sinatra_dev/__init__.py +71 -0
  2. sinatra_dev/_client.py +391 -0
  3. sinatra_dev/_errors.py +53 -0
  4. sinatra_dev/_generated/__init__.py +8 -0
  5. sinatra_dev/_generated/api/__init__.py +1 -0
  6. sinatra_dev/_generated/api/me/__init__.py +1 -0
  7. sinatra_dev/_generated/api/me/me_get.py +156 -0
  8. sinatra_dev/_generated/api/me/me_revoke_token.py +173 -0
  9. sinatra_dev/_generated/api/messages/__init__.py +1 -0
  10. sinatra_dev/_generated/api/messages/messages_create.py +323 -0
  11. sinatra_dev/_generated/api/messages/messages_list.py +332 -0
  12. sinatra_dev/_generated/api/tasks/__init__.py +1 -0
  13. sinatra_dev/_generated/api/tasks/tasks_create.py +337 -0
  14. sinatra_dev/_generated/api/tasks/tasks_get.py +202 -0
  15. sinatra_dev/_generated/api/tasks/tasks_list.py +252 -0
  16. sinatra_dev/_generated/api/tasks/tasks_stop.py +231 -0
  17. sinatra_dev/_generated/api/tickets/__init__.py +1 -0
  18. sinatra_dev/_generated/api/tickets/tickets_delegate.py +273 -0
  19. sinatra_dev/_generated/api/tickets/tickets_search.py +265 -0
  20. sinatra_dev/_generated/client.py +280 -0
  21. sinatra_dev/_generated/errors.py +16 -0
  22. sinatra_dev/_generated/models/__init__.py +47 -0
  23. sinatra_dev/_generated/models/create_message_request.py +62 -0
  24. sinatra_dev/_generated/models/create_message_response.py +62 -0
  25. sinatra_dev/_generated/models/create_task_request.py +106 -0
  26. sinatra_dev/_generated/models/create_task_response.py +76 -0
  27. sinatra_dev/_generated/models/delegate_github_ticket.py +86 -0
  28. sinatra_dev/_generated/models/delegate_linear_ticket.py +78 -0
  29. sinatra_dev/_generated/models/delegate_ticket_response.py +62 -0
  30. sinatra_dev/_generated/models/error.py +168 -0
  31. sinatra_dev/_generated/models/me.py +86 -0
  32. sinatra_dev/_generated/models/me_user.py +98 -0
  33. sinatra_dev/_generated/models/message.py +113 -0
  34. sinatra_dev/_generated/models/message_list.py +91 -0
  35. sinatra_dev/_generated/models/pull_request.py +70 -0
  36. sinatra_dev/_generated/models/stop_task_response.py +62 -0
  37. sinatra_dev/_generated/models/task.py +201 -0
  38. sinatra_dev/_generated/models/task_list.py +91 -0
  39. sinatra_dev/_generated/models/ticket.py +147 -0
  40. sinatra_dev/_generated/models/ticket_search_result.py +97 -0
  41. sinatra_dev/_generated/models/ticket_search_result_github.py +77 -0
  42. sinatra_dev/_generated/models/ticket_search_result_linear.py +79 -0
  43. sinatra_dev/_generated/models/workspace.py +78 -0
  44. sinatra_dev/_generated/types.py +54 -0
  45. sinatra_dev/_retry.py +72 -0
  46. sinatra_dev/py.typed +0 -0
  47. sinatra_dev-0.1.0.dist-info/METADATA +184 -0
  48. sinatra_dev-0.1.0.dist-info/RECORD +50 -0
  49. sinatra_dev-0.1.0.dist-info/WHEEL +4 -0
  50. sinatra_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,332 @@
1
+ from http import HTTPStatus
2
+ from typing import Any
3
+ from urllib.parse import quote
4
+
5
+ import httpx
6
+
7
+ from ... import errors
8
+ from ...client import AuthenticatedClient, Client
9
+ from ...models.error import Error
10
+ from ...models.message_list import MessageList
11
+ from ...types import UNSET, Response, Unset
12
+
13
+
14
+ def _get_kwargs(
15
+ workspace_id: str,
16
+ task_id: str,
17
+ *,
18
+ after: str | Unset = UNSET,
19
+ limit: int | Unset = 100,
20
+ ) -> dict[str, Any]:
21
+
22
+ params: dict[str, Any] = {}
23
+
24
+ params["after"] = after
25
+
26
+ params["limit"] = limit
27
+
28
+ params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
29
+
30
+ _kwargs: dict[str, Any] = {
31
+ "method": "get",
32
+ "url": "/v1/workspaces/{workspace_id}/tasks/{task_id}/messages".format(
33
+ workspace_id=quote(str(workspace_id), safe=""),
34
+ task_id=quote(str(task_id), safe=""),
35
+ ),
36
+ "params": params,
37
+ }
38
+
39
+ return _kwargs
40
+
41
+
42
+ def _parse_response(
43
+ *, client: AuthenticatedClient | Client, response: httpx.Response
44
+ ) -> Error | MessageList | None:
45
+ if response.status_code == 200:
46
+ response_200 = MessageList.from_dict(response.json())
47
+
48
+ return response_200
49
+
50
+ if response.status_code == 400:
51
+ response_400 = Error.from_dict(response.json())
52
+
53
+ return response_400
54
+
55
+ if response.status_code == 401:
56
+ response_401 = Error.from_dict(response.json())
57
+
58
+ return response_401
59
+
60
+ if response.status_code == 404:
61
+ response_404 = Error.from_dict(response.json())
62
+
63
+ return response_404
64
+
65
+ if response.status_code == 409:
66
+ response_409 = Error.from_dict(response.json())
67
+
68
+ return response_409
69
+
70
+ if response.status_code == 429:
71
+ response_429 = Error.from_dict(response.json())
72
+
73
+ return response_429
74
+
75
+ if response.status_code == 500:
76
+ response_500 = Error.from_dict(response.json())
77
+
78
+ return response_500
79
+
80
+ if client.raise_on_unexpected_status:
81
+ raise errors.UnexpectedStatus(response.status_code, response.content)
82
+ else:
83
+ return None
84
+
85
+
86
+ def _build_response(
87
+ *, client: AuthenticatedClient | Client, response: httpx.Response
88
+ ) -> Response[Error | MessageList]:
89
+ return Response(
90
+ status_code=HTTPStatus(response.status_code),
91
+ content=response.content,
92
+ headers=response.headers,
93
+ parsed=_parse_response(client=client, response=response),
94
+ )
95
+
96
+
97
+ def sync_detailed(
98
+ workspace_id: str,
99
+ task_id: str,
100
+ *,
101
+ client: AuthenticatedClient | Client,
102
+ after: str | Unset = UNSET,
103
+ limit: int | Unset = 100,
104
+ ) -> Response[Error | MessageList]:
105
+ """Read the conversation
106
+
107
+ Returns the conversation of a task started from the API, CLI, MCP or dashboard, oldest first. A task
108
+ that started elsewhere has no conversation here (`409 not_an_api_task`); its conversation is where
109
+ it started.
110
+
111
+ The conversation holds final answers and questions, not progress. The agent posts one reply per
112
+ turn: its answer, or a question when it needs your input. Sinatra adds its own notices, such as
113
+ errors and refusals.
114
+
115
+ Each entry's `role` and `kind` say what it is:
116
+
117
+ - `user` and `message`: a message a person sent. `messageId` is its idempotency key, or a generated
118
+ id.
119
+ - `agent` and `response`: the agent's answer for a turn.
120
+ - `agent` and `clarify`: a question the agent needs answered to continue.
121
+ - `platform` and `response` or `error`: a message from Sinatra itself, such as a refusal, an error,
122
+ a stop, or files the agent produced.
123
+
124
+ When `next` isn't `null`, more entries follow: pass `next` as `after` to get the next page.
125
+
126
+ There is no streaming. To wait for a reply, poll this endpoint with `after` set to the `id` of the
127
+ last entry you read, and check the task's `status` with Get a task. The turn is done when the task
128
+ stops working, for example when it reaches `replied` or `pr_open`. While a task is queued, its
129
+ conversation stays quiet; its `queued` field says so.
130
+
131
+ Args:
132
+ workspace_id (str):
133
+ task_id (str):
134
+ after (str | Unset):
135
+ limit (int | Unset): Default: 100.
136
+
137
+ Raises:
138
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
139
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
140
+
141
+ Returns:
142
+ Response[Error | MessageList]
143
+ """
144
+
145
+ kwargs = _get_kwargs(
146
+ workspace_id=workspace_id,
147
+ task_id=task_id,
148
+ after=after,
149
+ limit=limit,
150
+ )
151
+
152
+ response = client.get_httpx_client().request(
153
+ **kwargs,
154
+ )
155
+
156
+ return _build_response(client=client, response=response)
157
+
158
+
159
+ def sync(
160
+ workspace_id: str,
161
+ task_id: str,
162
+ *,
163
+ client: AuthenticatedClient | Client,
164
+ after: str | Unset = UNSET,
165
+ limit: int | Unset = 100,
166
+ ) -> Error | MessageList | None:
167
+ """Read the conversation
168
+
169
+ Returns the conversation of a task started from the API, CLI, MCP or dashboard, oldest first. A task
170
+ that started elsewhere has no conversation here (`409 not_an_api_task`); its conversation is where
171
+ it started.
172
+
173
+ The conversation holds final answers and questions, not progress. The agent posts one reply per
174
+ turn: its answer, or a question when it needs your input. Sinatra adds its own notices, such as
175
+ errors and refusals.
176
+
177
+ Each entry's `role` and `kind` say what it is:
178
+
179
+ - `user` and `message`: a message a person sent. `messageId` is its idempotency key, or a generated
180
+ id.
181
+ - `agent` and `response`: the agent's answer for a turn.
182
+ - `agent` and `clarify`: a question the agent needs answered to continue.
183
+ - `platform` and `response` or `error`: a message from Sinatra itself, such as a refusal, an error,
184
+ a stop, or files the agent produced.
185
+
186
+ When `next` isn't `null`, more entries follow: pass `next` as `after` to get the next page.
187
+
188
+ There is no streaming. To wait for a reply, poll this endpoint with `after` set to the `id` of the
189
+ last entry you read, and check the task's `status` with Get a task. The turn is done when the task
190
+ stops working, for example when it reaches `replied` or `pr_open`. While a task is queued, its
191
+ conversation stays quiet; its `queued` field says so.
192
+
193
+ Args:
194
+ workspace_id (str):
195
+ task_id (str):
196
+ after (str | Unset):
197
+ limit (int | Unset): Default: 100.
198
+
199
+ Raises:
200
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
201
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
202
+
203
+ Returns:
204
+ Error | MessageList
205
+ """
206
+
207
+ return sync_detailed(
208
+ workspace_id=workspace_id,
209
+ task_id=task_id,
210
+ client=client,
211
+ after=after,
212
+ limit=limit,
213
+ ).parsed
214
+
215
+
216
+ async def asyncio_detailed(
217
+ workspace_id: str,
218
+ task_id: str,
219
+ *,
220
+ client: AuthenticatedClient | Client,
221
+ after: str | Unset = UNSET,
222
+ limit: int | Unset = 100,
223
+ ) -> Response[Error | MessageList]:
224
+ """Read the conversation
225
+
226
+ Returns the conversation of a task started from the API, CLI, MCP or dashboard, oldest first. A task
227
+ that started elsewhere has no conversation here (`409 not_an_api_task`); its conversation is where
228
+ it started.
229
+
230
+ The conversation holds final answers and questions, not progress. The agent posts one reply per
231
+ turn: its answer, or a question when it needs your input. Sinatra adds its own notices, such as
232
+ errors and refusals.
233
+
234
+ Each entry's `role` and `kind` say what it is:
235
+
236
+ - `user` and `message`: a message a person sent. `messageId` is its idempotency key, or a generated
237
+ id.
238
+ - `agent` and `response`: the agent's answer for a turn.
239
+ - `agent` and `clarify`: a question the agent needs answered to continue.
240
+ - `platform` and `response` or `error`: a message from Sinatra itself, such as a refusal, an error,
241
+ a stop, or files the agent produced.
242
+
243
+ When `next` isn't `null`, more entries follow: pass `next` as `after` to get the next page.
244
+
245
+ There is no streaming. To wait for a reply, poll this endpoint with `after` set to the `id` of the
246
+ last entry you read, and check the task's `status` with Get a task. The turn is done when the task
247
+ stops working, for example when it reaches `replied` or `pr_open`. While a task is queued, its
248
+ conversation stays quiet; its `queued` field says so.
249
+
250
+ Args:
251
+ workspace_id (str):
252
+ task_id (str):
253
+ after (str | Unset):
254
+ limit (int | Unset): Default: 100.
255
+
256
+ Raises:
257
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
258
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
259
+
260
+ Returns:
261
+ Response[Error | MessageList]
262
+ """
263
+
264
+ kwargs = _get_kwargs(
265
+ workspace_id=workspace_id,
266
+ task_id=task_id,
267
+ after=after,
268
+ limit=limit,
269
+ )
270
+
271
+ response = await client.get_async_httpx_client().request(**kwargs)
272
+
273
+ return _build_response(client=client, response=response)
274
+
275
+
276
+ async def asyncio(
277
+ workspace_id: str,
278
+ task_id: str,
279
+ *,
280
+ client: AuthenticatedClient | Client,
281
+ after: str | Unset = UNSET,
282
+ limit: int | Unset = 100,
283
+ ) -> Error | MessageList | None:
284
+ """Read the conversation
285
+
286
+ Returns the conversation of a task started from the API, CLI, MCP or dashboard, oldest first. A task
287
+ that started elsewhere has no conversation here (`409 not_an_api_task`); its conversation is where
288
+ it started.
289
+
290
+ The conversation holds final answers and questions, not progress. The agent posts one reply per
291
+ turn: its answer, or a question when it needs your input. Sinatra adds its own notices, such as
292
+ errors and refusals.
293
+
294
+ Each entry's `role` and `kind` say what it is:
295
+
296
+ - `user` and `message`: a message a person sent. `messageId` is its idempotency key, or a generated
297
+ id.
298
+ - `agent` and `response`: the agent's answer for a turn.
299
+ - `agent` and `clarify`: a question the agent needs answered to continue.
300
+ - `platform` and `response` or `error`: a message from Sinatra itself, such as a refusal, an error,
301
+ a stop, or files the agent produced.
302
+
303
+ When `next` isn't `null`, more entries follow: pass `next` as `after` to get the next page.
304
+
305
+ There is no streaming. To wait for a reply, poll this endpoint with `after` set to the `id` of the
306
+ last entry you read, and check the task's `status` with Get a task. The turn is done when the task
307
+ stops working, for example when it reaches `replied` or `pr_open`. While a task is queued, its
308
+ conversation stays quiet; its `queued` field says so.
309
+
310
+ Args:
311
+ workspace_id (str):
312
+ task_id (str):
313
+ after (str | Unset):
314
+ limit (int | Unset): Default: 100.
315
+
316
+ Raises:
317
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
318
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
319
+
320
+ Returns:
321
+ Error | MessageList
322
+ """
323
+
324
+ return (
325
+ await asyncio_detailed(
326
+ workspace_id=workspace_id,
327
+ task_id=task_id,
328
+ client=client,
329
+ after=after,
330
+ limit=limit,
331
+ )
332
+ ).parsed
@@ -0,0 +1 @@
1
+ """Contains endpoint functions for accessing the API"""
@@ -0,0 +1,337 @@
1
+ from http import HTTPStatus
2
+ from typing import Any
3
+ from urllib.parse import quote
4
+
5
+ import httpx
6
+
7
+ from ... import errors
8
+ from ...client import AuthenticatedClient, Client
9
+ from ...models.create_task_request import CreateTaskRequest
10
+ from ...models.create_task_response import CreateTaskResponse
11
+ from ...models.error import Error
12
+ from ...types import UNSET, Response, Unset
13
+
14
+
15
+ def _get_kwargs(
16
+ workspace_id: str,
17
+ *,
18
+ body: CreateTaskRequest,
19
+ idempotency_key: str | Unset = UNSET,
20
+ ) -> dict[str, Any]:
21
+ headers: dict[str, Any] = {}
22
+ if not isinstance(idempotency_key, Unset):
23
+ headers["Idempotency-Key"] = idempotency_key
24
+
25
+ _kwargs: dict[str, Any] = {
26
+ "method": "post",
27
+ "url": "/v1/workspaces/{workspace_id}/tasks".format(
28
+ workspace_id=quote(str(workspace_id), safe=""),
29
+ ),
30
+ }
31
+
32
+ _kwargs["json"] = body.to_dict()
33
+
34
+ headers["Content-Type"] = "application/json"
35
+
36
+ _kwargs["headers"] = headers
37
+ return _kwargs
38
+
39
+
40
+ def _parse_response(
41
+ *, client: AuthenticatedClient | Client, response: httpx.Response
42
+ ) -> CreateTaskResponse | Error | None:
43
+ if response.status_code == 202:
44
+ response_202 = CreateTaskResponse.from_dict(response.json())
45
+
46
+ return response_202
47
+
48
+ if response.status_code == 400:
49
+ response_400 = Error.from_dict(response.json())
50
+
51
+ return response_400
52
+
53
+ if response.status_code == 401:
54
+ response_401 = Error.from_dict(response.json())
55
+
56
+ return response_401
57
+
58
+ if response.status_code == 404:
59
+ response_404 = Error.from_dict(response.json())
60
+
61
+ return response_404
62
+
63
+ if response.status_code == 409:
64
+ response_409 = Error.from_dict(response.json())
65
+
66
+ return response_409
67
+
68
+ if response.status_code == 413:
69
+ response_413 = Error.from_dict(response.json())
70
+
71
+ return response_413
72
+
73
+ if response.status_code == 415:
74
+ response_415 = Error.from_dict(response.json())
75
+
76
+ return response_415
77
+
78
+ if response.status_code == 429:
79
+ response_429 = Error.from_dict(response.json())
80
+
81
+ return response_429
82
+
83
+ if response.status_code == 500:
84
+ response_500 = Error.from_dict(response.json())
85
+
86
+ return response_500
87
+
88
+ if response.status_code == 503:
89
+ response_503 = Error.from_dict(response.json())
90
+
91
+ return response_503
92
+
93
+ if client.raise_on_unexpected_status:
94
+ raise errors.UnexpectedStatus(response.status_code, response.content)
95
+ else:
96
+ return None
97
+
98
+
99
+ def _build_response(
100
+ *, client: AuthenticatedClient | Client, response: httpx.Response
101
+ ) -> Response[CreateTaskResponse | Error]:
102
+ return Response(
103
+ status_code=HTTPStatus(response.status_code),
104
+ content=response.content,
105
+ headers=response.headers,
106
+ parsed=_parse_response(client=client, response=response),
107
+ )
108
+
109
+
110
+ def sync_detailed(
111
+ workspace_id: str,
112
+ *,
113
+ client: AuthenticatedClient | Client,
114
+ body: CreateTaskRequest,
115
+ idempotency_key: str | Unset = UNSET,
116
+ ) -> Response[CreateTaskResponse | Error]:
117
+ """Start a task
118
+
119
+ Starts a task and returns `202` with the task and the id of your first message. The task runs like
120
+ any other: it gets a sandbox, can open a pull request, and shows up in the dashboard.
121
+
122
+ Only `prompt` is required. `title` defaults to the prompt's first line, up to 80 characters. `repo`
123
+ is optional: omit it to let the agent choose a repository.
124
+
125
+ Before it makes the task, Sinatra checks that the workspace is connected to GitHub, that billing
126
+ allows the task, and that the queue has room. A refusal says which in its `error` code, and a retry
127
+ with the same key of a start that already made its task is never refused.
128
+
129
+ Send an `Idempotency-Key` so a retry is safe. A start retried with the same key returns the same
130
+ task and never starts a second one. The same key with a different prompt, or a different repository
131
+ or title if you send them, is `409 idempotency_conflict`. Keys belong to the person who sent them.
132
+ The key becomes the first message's `messageId`.
133
+
134
+ If Sinatra saves your request but can't hand it to the agent yet, it answers `503 unavailable` with
135
+ the new `taskId` and `messageId`. Retry with the same key, or send a message on that task, which
136
+ sends your prompt first.
137
+
138
+ A task started this way counts against the workspace's plan like any other. A running turn takes one
139
+ of the plan's tasks at a time; past that it's queued. See [How the queue
140
+ works](https://docs.sinatra.dev/docs/managed/billing#how-the-queue-works). It also needs model
141
+ access, like a task delegated from Linear or GitHub. See
142
+ [Models](https://docs.sinatra.dev/docs/configuration/models).
143
+
144
+ Args:
145
+ workspace_id (str):
146
+ idempotency_key (str | Unset):
147
+ body (CreateTaskRequest): A task to start.
148
+
149
+ Raises:
150
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
151
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
152
+
153
+ Returns:
154
+ Response[CreateTaskResponse | Error]
155
+ """
156
+
157
+ kwargs = _get_kwargs(
158
+ workspace_id=workspace_id,
159
+ body=body,
160
+ idempotency_key=idempotency_key,
161
+ )
162
+
163
+ response = client.get_httpx_client().request(
164
+ **kwargs,
165
+ )
166
+
167
+ return _build_response(client=client, response=response)
168
+
169
+
170
+ def sync(
171
+ workspace_id: str,
172
+ *,
173
+ client: AuthenticatedClient | Client,
174
+ body: CreateTaskRequest,
175
+ idempotency_key: str | Unset = UNSET,
176
+ ) -> CreateTaskResponse | Error | None:
177
+ """Start a task
178
+
179
+ Starts a task and returns `202` with the task and the id of your first message. The task runs like
180
+ any other: it gets a sandbox, can open a pull request, and shows up in the dashboard.
181
+
182
+ Only `prompt` is required. `title` defaults to the prompt's first line, up to 80 characters. `repo`
183
+ is optional: omit it to let the agent choose a repository.
184
+
185
+ Before it makes the task, Sinatra checks that the workspace is connected to GitHub, that billing
186
+ allows the task, and that the queue has room. A refusal says which in its `error` code, and a retry
187
+ with the same key of a start that already made its task is never refused.
188
+
189
+ Send an `Idempotency-Key` so a retry is safe. A start retried with the same key returns the same
190
+ task and never starts a second one. The same key with a different prompt, or a different repository
191
+ or title if you send them, is `409 idempotency_conflict`. Keys belong to the person who sent them.
192
+ The key becomes the first message's `messageId`.
193
+
194
+ If Sinatra saves your request but can't hand it to the agent yet, it answers `503 unavailable` with
195
+ the new `taskId` and `messageId`. Retry with the same key, or send a message on that task, which
196
+ sends your prompt first.
197
+
198
+ A task started this way counts against the workspace's plan like any other. A running turn takes one
199
+ of the plan's tasks at a time; past that it's queued. See [How the queue
200
+ works](https://docs.sinatra.dev/docs/managed/billing#how-the-queue-works). It also needs model
201
+ access, like a task delegated from Linear or GitHub. See
202
+ [Models](https://docs.sinatra.dev/docs/configuration/models).
203
+
204
+ Args:
205
+ workspace_id (str):
206
+ idempotency_key (str | Unset):
207
+ body (CreateTaskRequest): A task to start.
208
+
209
+ Raises:
210
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
211
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
212
+
213
+ Returns:
214
+ CreateTaskResponse | Error
215
+ """
216
+
217
+ return sync_detailed(
218
+ workspace_id=workspace_id,
219
+ client=client,
220
+ body=body,
221
+ idempotency_key=idempotency_key,
222
+ ).parsed
223
+
224
+
225
+ async def asyncio_detailed(
226
+ workspace_id: str,
227
+ *,
228
+ client: AuthenticatedClient | Client,
229
+ body: CreateTaskRequest,
230
+ idempotency_key: str | Unset = UNSET,
231
+ ) -> Response[CreateTaskResponse | Error]:
232
+ """Start a task
233
+
234
+ Starts a task and returns `202` with the task and the id of your first message. The task runs like
235
+ any other: it gets a sandbox, can open a pull request, and shows up in the dashboard.
236
+
237
+ Only `prompt` is required. `title` defaults to the prompt's first line, up to 80 characters. `repo`
238
+ is optional: omit it to let the agent choose a repository.
239
+
240
+ Before it makes the task, Sinatra checks that the workspace is connected to GitHub, that billing
241
+ allows the task, and that the queue has room. A refusal says which in its `error` code, and a retry
242
+ with the same key of a start that already made its task is never refused.
243
+
244
+ Send an `Idempotency-Key` so a retry is safe. A start retried with the same key returns the same
245
+ task and never starts a second one. The same key with a different prompt, or a different repository
246
+ or title if you send them, is `409 idempotency_conflict`. Keys belong to the person who sent them.
247
+ The key becomes the first message's `messageId`.
248
+
249
+ If Sinatra saves your request but can't hand it to the agent yet, it answers `503 unavailable` with
250
+ the new `taskId` and `messageId`. Retry with the same key, or send a message on that task, which
251
+ sends your prompt first.
252
+
253
+ A task started this way counts against the workspace's plan like any other. A running turn takes one
254
+ of the plan's tasks at a time; past that it's queued. See [How the queue
255
+ works](https://docs.sinatra.dev/docs/managed/billing#how-the-queue-works). It also needs model
256
+ access, like a task delegated from Linear or GitHub. See
257
+ [Models](https://docs.sinatra.dev/docs/configuration/models).
258
+
259
+ Args:
260
+ workspace_id (str):
261
+ idempotency_key (str | Unset):
262
+ body (CreateTaskRequest): A task to start.
263
+
264
+ Raises:
265
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
266
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
267
+
268
+ Returns:
269
+ Response[CreateTaskResponse | Error]
270
+ """
271
+
272
+ kwargs = _get_kwargs(
273
+ workspace_id=workspace_id,
274
+ body=body,
275
+ idempotency_key=idempotency_key,
276
+ )
277
+
278
+ response = await client.get_async_httpx_client().request(**kwargs)
279
+
280
+ return _build_response(client=client, response=response)
281
+
282
+
283
+ async def asyncio(
284
+ workspace_id: str,
285
+ *,
286
+ client: AuthenticatedClient | Client,
287
+ body: CreateTaskRequest,
288
+ idempotency_key: str | Unset = UNSET,
289
+ ) -> CreateTaskResponse | Error | None:
290
+ """Start a task
291
+
292
+ Starts a task and returns `202` with the task and the id of your first message. The task runs like
293
+ any other: it gets a sandbox, can open a pull request, and shows up in the dashboard.
294
+
295
+ Only `prompt` is required. `title` defaults to the prompt's first line, up to 80 characters. `repo`
296
+ is optional: omit it to let the agent choose a repository.
297
+
298
+ Before it makes the task, Sinatra checks that the workspace is connected to GitHub, that billing
299
+ allows the task, and that the queue has room. A refusal says which in its `error` code, and a retry
300
+ with the same key of a start that already made its task is never refused.
301
+
302
+ Send an `Idempotency-Key` so a retry is safe. A start retried with the same key returns the same
303
+ task and never starts a second one. The same key with a different prompt, or a different repository
304
+ or title if you send them, is `409 idempotency_conflict`. Keys belong to the person who sent them.
305
+ The key becomes the first message's `messageId`.
306
+
307
+ If Sinatra saves your request but can't hand it to the agent yet, it answers `503 unavailable` with
308
+ the new `taskId` and `messageId`. Retry with the same key, or send a message on that task, which
309
+ sends your prompt first.
310
+
311
+ A task started this way counts against the workspace's plan like any other. A running turn takes one
312
+ of the plan's tasks at a time; past that it's queued. See [How the queue
313
+ works](https://docs.sinatra.dev/docs/managed/billing#how-the-queue-works). It also needs model
314
+ access, like a task delegated from Linear or GitHub. See
315
+ [Models](https://docs.sinatra.dev/docs/configuration/models).
316
+
317
+ Args:
318
+ workspace_id (str):
319
+ idempotency_key (str | Unset):
320
+ body (CreateTaskRequest): A task to start.
321
+
322
+ Raises:
323
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
324
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
325
+
326
+ Returns:
327
+ CreateTaskResponse | Error
328
+ """
329
+
330
+ return (
331
+ await asyncio_detailed(
332
+ workspace_id=workspace_id,
333
+ client=client,
334
+ body=body,
335
+ idempotency_key=idempotency_key,
336
+ )
337
+ ).parsed