indent 0.1.26__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 (55) hide show
  1. exponent/__init__.py +34 -0
  2. exponent/cli.py +110 -0
  3. exponent/commands/cloud_commands.py +585 -0
  4. exponent/commands/common.py +411 -0
  5. exponent/commands/config_commands.py +334 -0
  6. exponent/commands/run_commands.py +222 -0
  7. exponent/commands/settings.py +56 -0
  8. exponent/commands/types.py +111 -0
  9. exponent/commands/upgrade.py +29 -0
  10. exponent/commands/utils.py +146 -0
  11. exponent/core/config.py +180 -0
  12. exponent/core/graphql/__init__.py +0 -0
  13. exponent/core/graphql/client.py +61 -0
  14. exponent/core/graphql/get_chats_query.py +47 -0
  15. exponent/core/graphql/mutations.py +160 -0
  16. exponent/core/graphql/queries.py +146 -0
  17. exponent/core/graphql/subscriptions.py +16 -0
  18. exponent/core/remote_execution/checkpoints.py +212 -0
  19. exponent/core/remote_execution/cli_rpc_types.py +499 -0
  20. exponent/core/remote_execution/client.py +999 -0
  21. exponent/core/remote_execution/code_execution.py +77 -0
  22. exponent/core/remote_execution/default_env.py +31 -0
  23. exponent/core/remote_execution/error_info.py +45 -0
  24. exponent/core/remote_execution/exceptions.py +10 -0
  25. exponent/core/remote_execution/file_write.py +35 -0
  26. exponent/core/remote_execution/files.py +330 -0
  27. exponent/core/remote_execution/git.py +268 -0
  28. exponent/core/remote_execution/http_fetch.py +94 -0
  29. exponent/core/remote_execution/languages/python_execution.py +239 -0
  30. exponent/core/remote_execution/languages/shell_streaming.py +226 -0
  31. exponent/core/remote_execution/languages/types.py +20 -0
  32. exponent/core/remote_execution/port_utils.py +73 -0
  33. exponent/core/remote_execution/session.py +128 -0
  34. exponent/core/remote_execution/system_context.py +26 -0
  35. exponent/core/remote_execution/terminal_session.py +375 -0
  36. exponent/core/remote_execution/terminal_types.py +29 -0
  37. exponent/core/remote_execution/tool_execution.py +595 -0
  38. exponent/core/remote_execution/tool_type_utils.py +39 -0
  39. exponent/core/remote_execution/truncation.py +296 -0
  40. exponent/core/remote_execution/types.py +635 -0
  41. exponent/core/remote_execution/utils.py +477 -0
  42. exponent/core/types/__init__.py +0 -0
  43. exponent/core/types/command_data.py +206 -0
  44. exponent/core/types/event_types.py +89 -0
  45. exponent/core/types/generated/__init__.py +0 -0
  46. exponent/core/types/generated/strategy_info.py +213 -0
  47. exponent/migration-docs/login.md +112 -0
  48. exponent/py.typed +4 -0
  49. exponent/utils/__init__.py +0 -0
  50. exponent/utils/colors.py +92 -0
  51. exponent/utils/version.py +289 -0
  52. indent-0.1.26.dist-info/METADATA +38 -0
  53. indent-0.1.26.dist-info/RECORD +55 -0
  54. indent-0.1.26.dist-info/WHEEL +4 -0
  55. indent-0.1.26.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,499 @@
1
+ from typing import TYPE_CHECKING, Any
2
+
3
+ import msgspec
4
+ import yaml
5
+ from pydantic_ai.format_as_xml import format_as_xml
6
+
7
+ if TYPE_CHECKING:
8
+ from exponent_server.core.tools.edit_tool import (
9
+ EditToolInput as ServerSideEditToolInput,
10
+ )
11
+
12
+
13
+ class PartialToolResult(msgspec.Struct, tag_field="tool_name", omit_defaults=True):
14
+ pass
15
+
16
+
17
+ class ToolInput(msgspec.Struct, tag_field="tool_name", omit_defaults=True):
18
+ """Concrete subclasses describe the full input schema for a tool."""
19
+
20
+ def to_llm(self) -> dict[str, Any]:
21
+ """Convert ToolInput to LLM-friendly typed dict format.
22
+
23
+ Returns a dictionary with the tool parameters, excluding the tool_name
24
+ which is handled separately by the LLM integration layer.
25
+
26
+ Returns:
27
+ self by default, which msgspec will serialize appropriately.
28
+ """
29
+ return msgspec.to_builtins(self) # type: ignore[no-any-return]
30
+
31
+ def to_text(self) -> str:
32
+ """This text is purely used for human debugging, it's not fed to the llm"""
33
+ d = msgspec.to_builtins(self)
34
+ del d["tool_name"]
35
+ return yaml.dump(d)
36
+
37
+
38
+ class ToolResult(msgspec.Struct, tag_field="tool_name", omit_defaults=True):
39
+ """Concrete subclasses return data from a tool execution."""
40
+
41
+ def to_text(self) -> str:
42
+ """
43
+ This provides a default textual representation of the tool result. Override it as needed for your tool."""
44
+ d = msgspec.to_builtins(self)
45
+ del d["tool_name"]
46
+ return format_as_xml(d, include_root_tag=False, item_tag="item")
47
+
48
+
49
+ class ErrorToolResult(ToolResult, tag="error"):
50
+ error_message: str
51
+ is_assistant_error: bool = False
52
+
53
+
54
+ READ_TOOL_NAME = "read"
55
+ READ_TOOL_ARTIFACT_NAME = "read_tool_artifact"
56
+
57
+
58
+ class ReadToolInput(ToolInput, tag=READ_TOOL_NAME):
59
+ file_path: str
60
+ offset: int | None = None
61
+ limit: int | None = None
62
+
63
+
64
+ class FileMetadata(msgspec.Struct):
65
+ modified_timestamp: float
66
+ file_mode: str
67
+
68
+
69
+ class ReadToolArtifactResult(ToolResult, tag=READ_TOOL_ARTIFACT_NAME):
70
+ s3_uri: str
71
+ file_path: str
72
+ media_type: str
73
+
74
+ def to_text(self) -> str:
75
+ return f"[Image artifact uploaded to {self.s3_uri}]"
76
+
77
+
78
+ class ReadToolResult(ToolResult, tag=READ_TOOL_NAME):
79
+ content: str | None = None
80
+ num_lines: int | None = None
81
+ start_line: int | None = None
82
+ total_lines: int | None = None
83
+ metadata: FileMetadata | None = None
84
+ artifact: ReadToolArtifactResult | None = None
85
+
86
+ def to_text(self) -> str:
87
+ if self.artifact:
88
+ return self.artifact.to_text()
89
+ assert self.content is not None
90
+ assert self.start_line is not None
91
+ lines = self.content.splitlines()
92
+ lines = [
93
+ f"{str(i).rjust(6)}→{line}"
94
+ for i, line in enumerate(lines, start=self.start_line + 1)
95
+ ]
96
+ return "\n".join(lines)
97
+
98
+
99
+ LIST_TOOL_NAME = "ls"
100
+
101
+
102
+ class ListToolInput(ToolInput, tag=LIST_TOOL_NAME):
103
+ path: str
104
+ ignore: list[str] | None = None
105
+
106
+
107
+ class ListToolResult(ToolResult, tag=LIST_TOOL_NAME):
108
+ files: list[str]
109
+ truncated: bool = False
110
+
111
+
112
+ GLOB_TOOL_NAME = "glob"
113
+
114
+
115
+ class GlobToolInput(ToolInput, tag=GLOB_TOOL_NAME):
116
+ pattern: str
117
+ path: str | None = None
118
+
119
+
120
+ class GlobToolResult(ToolResult, tag=GLOB_TOOL_NAME):
121
+ filenames: list[str]
122
+ duration_ms: int
123
+ num_files: int
124
+ truncated: bool
125
+
126
+
127
+ WRITE_TOOL_NAME = "write"
128
+
129
+
130
+ class WriteToolInput(ToolInput, tag=WRITE_TOOL_NAME):
131
+ file_path: str
132
+ content: str
133
+
134
+
135
+ class WriteToolResult(ToolResult, tag=WRITE_TOOL_NAME):
136
+ message: str
137
+ metadata: FileMetadata | None = None
138
+
139
+
140
+ GREP_TOOL_NAME = "grep"
141
+
142
+
143
+ class GrepToolInput(ToolInput, tag=GREP_TOOL_NAME):
144
+ pattern: str
145
+ path: str | None = None
146
+ include: str | None = None
147
+ multiline: bool | None = None
148
+
149
+
150
+ class GrepToolResult(ToolResult, tag=GREP_TOOL_NAME):
151
+ matches: list[str]
152
+ truncated: bool = False
153
+
154
+
155
+ EDIT_TOOL_NAME = "edit"
156
+
157
+
158
+ # This is only used in the CLI. The server side type is edit_tool.py
159
+ class EditToolInput(ToolInput, tag=EDIT_TOOL_NAME):
160
+ file_path: str
161
+ old_string: str
162
+ new_string: str
163
+ replace_all: bool = False
164
+ # The last retrieved modified timestamp. This is used to check if the file has been modified since the last read/write that we are aware of (in which case we should re-read the file before editing).
165
+ last_known_modified_timestamp: float | None = None
166
+
167
+ @classmethod
168
+ def from_server_side_input(
169
+ cls,
170
+ server_side_input: "ServerSideEditToolInput",
171
+ last_known_modified_timestamp: float | None,
172
+ ) -> "EditToolInput":
173
+ return cls(
174
+ file_path=server_side_input.file_path,
175
+ old_string=server_side_input.old_string,
176
+ new_string=server_side_input.new_string,
177
+ replace_all=server_side_input.replace_all,
178
+ last_known_modified_timestamp=last_known_modified_timestamp,
179
+ )
180
+
181
+
182
+ class EditToolResult(ToolResult, tag=EDIT_TOOL_NAME):
183
+ message: str
184
+ metadata: FileMetadata | None = None
185
+
186
+
187
+ BASH_TOOL_NAME = "bash"
188
+
189
+
190
+ class BashToolInput(ToolInput, tag=BASH_TOOL_NAME):
191
+ command: str
192
+ timeout: int | None = None
193
+ description: str | None = None
194
+
195
+
196
+ class PartialBashToolResult(PartialToolResult, tag=BASH_TOOL_NAME):
197
+ shell_output: str | None = None
198
+
199
+
200
+ class BashToolResult(ToolResult, tag=BASH_TOOL_NAME):
201
+ shell_output: str
202
+ duration_ms: int
203
+ exit_code: int | None
204
+ timed_out: bool
205
+ stopped_by_user: bool
206
+
207
+
208
+ DOWNLOAD_ARTIFACT_TOOL_NAME = "download_artifact"
209
+
210
+
211
+ class DownloadArtifactToolInput(ToolInput, tag=DOWNLOAD_ARTIFACT_TOOL_NAME):
212
+ presigned_url: str
213
+ file_path: str
214
+ artifact_id: str
215
+ overwrite: bool = True
216
+
217
+
218
+ class DownloadArtifactToolResult(ToolResult, tag=DOWNLOAD_ARTIFACT_TOOL_NAME):
219
+ file_path: str
220
+ artifact_id: str
221
+ file_size_bytes: int
222
+ content_preview: str | None = None
223
+ num_lines: int | None = None
224
+ total_lines: int | None = None
225
+ truncated: bool = False
226
+
227
+
228
+ UPLOAD_ARTIFACT_TOOL_NAME = "upload_artifact"
229
+
230
+
231
+ class UploadArtifactToolInput(ToolInput, tag=UPLOAD_ARTIFACT_TOOL_NAME):
232
+ presigned_url: str
233
+ file_path: str
234
+ artifact_id: str
235
+ content_type: str
236
+
237
+
238
+ class UploadArtifactToolResult(ToolResult, tag=UPLOAD_ARTIFACT_TOOL_NAME):
239
+ artifact_id: str
240
+ file_size_bytes: int
241
+ content_type: str
242
+
243
+
244
+ class HttpRequest(msgspec.Struct, tag="http_fetch_cli"):
245
+ url: str
246
+ method: str = "GET"
247
+ headers: dict[str, str] | None = None
248
+ timeout: int | None = None
249
+
250
+
251
+ class HttpResponse(msgspec.Struct, tag="http_fetch_cli"):
252
+ status_code: int | None = None
253
+ content: str | None = None
254
+ error_message: str | None = None
255
+ duration_ms: int | None = None
256
+ headers: dict[str, str] | None = None
257
+
258
+
259
+ ToolInputType = (
260
+ ReadToolInput
261
+ | WriteToolInput
262
+ | ListToolInput
263
+ | GlobToolInput
264
+ | GrepToolInput
265
+ | EditToolInput
266
+ | BashToolInput
267
+ | DownloadArtifactToolInput
268
+ | UploadArtifactToolInput
269
+ )
270
+ PartialToolResultType = PartialBashToolResult
271
+
272
+ ToolResultType = (
273
+ ReadToolResult
274
+ | ReadToolArtifactResult
275
+ | WriteToolResult
276
+ | ListToolResult
277
+ | GlobToolResult
278
+ | GrepToolResult
279
+ | EditToolResult
280
+ | BashToolResult
281
+ | DownloadArtifactToolResult
282
+ | UploadArtifactToolResult
283
+ | ErrorToolResult
284
+ )
285
+
286
+
287
+ class ToolExecutionRequest(msgspec.Struct, tag="tool_execution"):
288
+ tool_input: ToolInputType
289
+
290
+
291
+ class GetAllFilesRequest(msgspec.Struct, tag="get_all_files"):
292
+ pass
293
+
294
+
295
+ class TerminateRequest(msgspec.Struct, tag="terminate"):
296
+ pass
297
+
298
+
299
+ class SwitchCLIChatRequest(msgspec.Struct, tag="switch_cli_chat"):
300
+ new_chat_uuid: str
301
+
302
+
303
+ # This message is sent periodically from the client to keep the connection alive while the chat is turning.
304
+ # This synergizes with CLI-side timeouts to avoid disconnecting during long operations.
305
+ class KeepAliveCliChatRequest(msgspec.Struct, tag="keep_alive_cli_chat"):
306
+ pass
307
+
308
+
309
+ class BatchToolExecutionRequest(msgspec.Struct, tag="batch_tool_execution"):
310
+ tool_inputs: list[ToolInputType]
311
+
312
+
313
+ class GetAllFilesResponse(msgspec.Struct, tag="get_all_files"):
314
+ files: list[str]
315
+
316
+
317
+ class TerminateResponse(msgspec.Struct, tag="terminate"):
318
+ pass
319
+
320
+
321
+ class TimeoutResponse(msgspec.Struct, tag="timeout"):
322
+ pass
323
+
324
+
325
+ class BatchToolExecutionResponse(msgspec.Struct, tag="batch_tool_execution"):
326
+ tool_results: list[ToolResultType]
327
+
328
+
329
+ class SwitchCLIChatResponse(msgspec.Struct, tag="switch_cli_chat"):
330
+ pass
331
+
332
+
333
+ class KeepAliveCliChatResponse(msgspec.Struct, tag="keep_alive_cli_chat"):
334
+ pass
335
+
336
+
337
+ class GenerateUploadUrlRequest(msgspec.Struct, tag="generate_upload_url"):
338
+ s3_key: str
339
+ content_type: str
340
+
341
+
342
+ class GenerateUploadUrlResponse(msgspec.Struct, tag="generate_upload_url"):
343
+ upload_url: str
344
+ s3_uri: str
345
+
346
+
347
+ # Terminal session management
348
+ class StartTerminalRequest(msgspec.Struct, tag="start_terminal"):
349
+ """Start a new terminal session with PTY"""
350
+
351
+ session_id: str
352
+ command: list[str] | None = None # None = default shell, or specific command
353
+ cols: int = 80
354
+ rows: int = 24
355
+ env: dict[str, str] | None = None # Additional environment variables
356
+
357
+
358
+ class StartTerminalResponse(msgspec.Struct, tag="start_terminal"):
359
+ """Response after starting terminal"""
360
+
361
+ session_id: str
362
+ success: bool
363
+ error_message: str | None = None
364
+
365
+
366
+ # Terminal input (user typing)
367
+ class TerminalInputRequest(msgspec.Struct, tag="terminal_input"):
368
+ """Send user input to terminal"""
369
+
370
+ session_id: str
371
+ data: str # Raw input data from user
372
+
373
+
374
+ class TerminalInputResponse(msgspec.Struct, tag="terminal_input"):
375
+ """Acknowledge input received"""
376
+
377
+ session_id: str
378
+ success: bool
379
+
380
+
381
+ # Terminal resize
382
+ class TerminalResizeRequest(msgspec.Struct, tag="terminal_resize"):
383
+ """Resize terminal dimensions"""
384
+
385
+ session_id: str
386
+ cols: int
387
+ rows: int
388
+
389
+
390
+ class TerminalResizeResponse(msgspec.Struct, tag="terminal_resize"):
391
+ """Acknowledge resize"""
392
+
393
+ session_id: str
394
+ success: bool
395
+
396
+
397
+ # Terminal stop
398
+ class StopTerminalRequest(msgspec.Struct, tag="stop_terminal"):
399
+ """Stop a terminal session"""
400
+
401
+ session_id: str
402
+
403
+
404
+ class StopTerminalResponse(msgspec.Struct, tag="stop_terminal"):
405
+ """Acknowledge terminal stopped"""
406
+
407
+ session_id: str
408
+ success: bool
409
+
410
+
411
+ class StreamingCodeExecutionRequest(msgspec.Struct, tag="streaming_code_execution"):
412
+ correlation_id: str
413
+ language: str # "python" or "shell"
414
+ content: str
415
+ timeout: int
416
+
417
+
418
+ class CliRpcRequest(msgspec.Struct):
419
+ request_id: str
420
+ request: (
421
+ ToolExecutionRequest
422
+ | GetAllFilesRequest
423
+ | TerminateRequest
424
+ | HttpRequest
425
+ | BatchToolExecutionRequest
426
+ | SwitchCLIChatRequest
427
+ | KeepAliveCliChatRequest
428
+ | GenerateUploadUrlRequest
429
+ | StartTerminalRequest
430
+ | TerminalInputRequest
431
+ | TerminalResizeRequest
432
+ | StopTerminalRequest
433
+ | StreamingCodeExecutionRequest
434
+ )
435
+
436
+
437
+ class ToolExecutionResponse(msgspec.Struct, tag="tool_execution"):
438
+ tool_result: ToolResultType
439
+
440
+
441
+ class ErrorResponse(msgspec.Struct, tag="error"):
442
+ error_message: str
443
+
444
+
445
+ class StreamingCodeExecutionResponseChunk(
446
+ msgspec.Struct, tag="streaming_code_execution_chunk"
447
+ ):
448
+ correlation_id: str
449
+ content: str
450
+ truncated: bool = False
451
+
452
+ def add(
453
+ self, new_chunk: "StreamingCodeExecutionResponseChunk"
454
+ ) -> "StreamingCodeExecutionResponseChunk":
455
+ """Aggregates content of this and a new chunk."""
456
+ assert self.correlation_id == new_chunk.correlation_id
457
+ return StreamingCodeExecutionResponseChunk(
458
+ correlation_id=self.correlation_id,
459
+ content=self.content + new_chunk.content,
460
+ truncated=self.truncated or new_chunk.truncated,
461
+ )
462
+
463
+
464
+ class StreamingCodeExecutionResponse(msgspec.Struct, tag="streaming_code_execution"):
465
+ correlation_id: str
466
+ content: str
467
+ truncated: bool = False
468
+ # Only present for shell code execution
469
+ cancelled_for_timeout: bool = False
470
+ exit_code: int | None = None
471
+ halted: bool = False
472
+
473
+
474
+ class StreamingErrorResponse(msgspec.Struct, tag="streaming_error"):
475
+ correlation_id: str
476
+ error_message: str
477
+
478
+
479
+ class CliRpcResponse(msgspec.Struct):
480
+ request_id: str
481
+ response: (
482
+ ToolExecutionResponse
483
+ | GetAllFilesResponse
484
+ | ErrorResponse
485
+ | TerminateResponse
486
+ | TimeoutResponse
487
+ | BatchToolExecutionResponse
488
+ | HttpResponse
489
+ | SwitchCLIChatResponse
490
+ | KeepAliveCliChatResponse
491
+ | GenerateUploadUrlResponse
492
+ | StartTerminalResponse
493
+ | TerminalInputResponse
494
+ | TerminalResizeResponse
495
+ | StopTerminalResponse
496
+ | StreamingCodeExecutionResponseChunk
497
+ | StreamingCodeExecutionResponse
498
+ | StreamingErrorResponse
499
+ )