rossum-agent 1.0.0rc0__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 (67) hide show
  1. rossum_agent/__init__.py +9 -0
  2. rossum_agent/agent/__init__.py +32 -0
  3. rossum_agent/agent/core.py +932 -0
  4. rossum_agent/agent/memory.py +176 -0
  5. rossum_agent/agent/models.py +160 -0
  6. rossum_agent/agent/request_classifier.py +152 -0
  7. rossum_agent/agent/skills.py +132 -0
  8. rossum_agent/agent/types.py +5 -0
  9. rossum_agent/agent_logging.py +56 -0
  10. rossum_agent/api/__init__.py +1 -0
  11. rossum_agent/api/cli.py +51 -0
  12. rossum_agent/api/dependencies.py +190 -0
  13. rossum_agent/api/main.py +180 -0
  14. rossum_agent/api/models/__init__.py +1 -0
  15. rossum_agent/api/models/schemas.py +301 -0
  16. rossum_agent/api/routes/__init__.py +1 -0
  17. rossum_agent/api/routes/chats.py +95 -0
  18. rossum_agent/api/routes/files.py +113 -0
  19. rossum_agent/api/routes/health.py +44 -0
  20. rossum_agent/api/routes/messages.py +218 -0
  21. rossum_agent/api/services/__init__.py +1 -0
  22. rossum_agent/api/services/agent_service.py +451 -0
  23. rossum_agent/api/services/chat_service.py +197 -0
  24. rossum_agent/api/services/file_service.py +65 -0
  25. rossum_agent/assets/Primary_light_logo.png +0 -0
  26. rossum_agent/bedrock_client.py +64 -0
  27. rossum_agent/prompts/__init__.py +27 -0
  28. rossum_agent/prompts/base_prompt.py +80 -0
  29. rossum_agent/prompts/system_prompt.py +24 -0
  30. rossum_agent/py.typed +0 -0
  31. rossum_agent/redis_storage.py +482 -0
  32. rossum_agent/rossum_mcp_integration.py +123 -0
  33. rossum_agent/skills/hook-debugging.md +31 -0
  34. rossum_agent/skills/organization-setup.md +60 -0
  35. rossum_agent/skills/rossum-deployment.md +102 -0
  36. rossum_agent/skills/schema-patching.md +61 -0
  37. rossum_agent/skills/schema-pruning.md +23 -0
  38. rossum_agent/skills/ui-settings.md +45 -0
  39. rossum_agent/streamlit_app/__init__.py +1 -0
  40. rossum_agent/streamlit_app/app.py +646 -0
  41. rossum_agent/streamlit_app/beep_sound.py +36 -0
  42. rossum_agent/streamlit_app/cli.py +17 -0
  43. rossum_agent/streamlit_app/render_modules.py +123 -0
  44. rossum_agent/streamlit_app/response_formatting.py +305 -0
  45. rossum_agent/tools/__init__.py +214 -0
  46. rossum_agent/tools/core.py +173 -0
  47. rossum_agent/tools/deploy.py +404 -0
  48. rossum_agent/tools/dynamic_tools.py +365 -0
  49. rossum_agent/tools/file_tools.py +62 -0
  50. rossum_agent/tools/formula.py +187 -0
  51. rossum_agent/tools/skills.py +31 -0
  52. rossum_agent/tools/spawn_mcp.py +227 -0
  53. rossum_agent/tools/subagents/__init__.py +31 -0
  54. rossum_agent/tools/subagents/base.py +303 -0
  55. rossum_agent/tools/subagents/hook_debug.py +591 -0
  56. rossum_agent/tools/subagents/knowledge_base.py +305 -0
  57. rossum_agent/tools/subagents/mcp_helpers.py +47 -0
  58. rossum_agent/tools/subagents/schema_patching.py +471 -0
  59. rossum_agent/url_context.py +167 -0
  60. rossum_agent/user_detection.py +100 -0
  61. rossum_agent/utils.py +128 -0
  62. rossum_agent-1.0.0rc0.dist-info/METADATA +311 -0
  63. rossum_agent-1.0.0rc0.dist-info/RECORD +67 -0
  64. rossum_agent-1.0.0rc0.dist-info/WHEEL +5 -0
  65. rossum_agent-1.0.0rc0.dist-info/entry_points.txt +3 -0
  66. rossum_agent-1.0.0rc0.dist-info/licenses/LICENSE +21 -0
  67. rossum_agent-1.0.0rc0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,173 @@
1
+ """Core module with shared types, callbacks, and MCP state management.
2
+
3
+ This module provides the foundational types and state management used by
4
+ all internal tools. Uses contextvars for thread-safe state management.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ from collections.abc import Callable
11
+ from contextvars import ContextVar
12
+ from dataclasses import dataclass, field
13
+ from pathlib import Path
14
+ from typing import TYPE_CHECKING
15
+
16
+ if TYPE_CHECKING:
17
+ import asyncio
18
+
19
+ from rossum_agent.rossum_mcp_integration import MCPConnection
20
+
21
+
22
+ @dataclass
23
+ class SubAgentProgress:
24
+ """Progress information from a sub-agent (e.g., debug_hook's Opus sub-agent)."""
25
+
26
+ tool_name: str
27
+ iteration: int
28
+ max_iterations: int
29
+ current_tool: str | None = None
30
+ tool_calls: list[str] = field(default_factory=list)
31
+ status: str = "running"
32
+
33
+
34
+ @dataclass
35
+ class SubAgentTokenUsage:
36
+ """Token usage from a sub-agent call."""
37
+
38
+ tool_name: str
39
+ input_tokens: int
40
+ output_tokens: int
41
+ iteration: int | None = None
42
+
43
+
44
+ @dataclass
45
+ class SubAgentText:
46
+ """Text output from a sub-agent (e.g., debug_hook's Opus sub-agent)."""
47
+
48
+ tool_name: str
49
+ text: str
50
+ is_final: bool = False
51
+
52
+
53
+ SubAgentProgressCallback = Callable[[SubAgentProgress], None]
54
+ SubAgentTextCallback = Callable[[SubAgentText], None]
55
+ SubAgentTokenCallback = Callable[[SubAgentTokenUsage], None]
56
+
57
+ # Context variables for thread-safe state management
58
+ _progress_callback: ContextVar[SubAgentProgressCallback | None] = ContextVar("progress_callback", default=None)
59
+ _text_callback: ContextVar[SubAgentTextCallback | None] = ContextVar("text_callback", default=None)
60
+ _token_callback: ContextVar[SubAgentTokenCallback | None] = ContextVar("token_callback", default=None)
61
+ _mcp_connection: ContextVar[MCPConnection | None] = ContextVar("mcp_connection", default=None)
62
+ _mcp_event_loop: ContextVar[asyncio.AbstractEventLoop | None] = ContextVar("mcp_event_loop", default=None)
63
+ _output_dir: ContextVar[Path | None] = ContextVar("output_dir", default=None)
64
+ _rossum_credentials: ContextVar[tuple[str, str] | None] = ContextVar("rossum_credentials", default=None)
65
+
66
+
67
+ def set_progress_callback(callback: SubAgentProgressCallback | None) -> None:
68
+ """Set the progress callback for sub-agent progress reporting."""
69
+ _progress_callback.set(callback)
70
+
71
+
72
+ def set_text_callback(callback: SubAgentTextCallback | None) -> None:
73
+ """Set the text callback for sub-agent text reporting."""
74
+ _text_callback.set(callback)
75
+
76
+
77
+ def set_token_callback(callback: SubAgentTokenCallback | None) -> None:
78
+ """Set the token callback for sub-agent token usage reporting."""
79
+ _token_callback.set(callback)
80
+
81
+
82
+ def report_progress(progress: SubAgentProgress) -> None:
83
+ """Report progress via the callback if set."""
84
+ if (callback := _progress_callback.get()) is not None:
85
+ callback(progress)
86
+
87
+
88
+ def report_text(text: SubAgentText) -> None:
89
+ """Report text via the callback if set."""
90
+ if (callback := _text_callback.get()) is not None:
91
+ callback(text)
92
+
93
+
94
+ def report_token_usage(usage: SubAgentTokenUsage) -> None:
95
+ """Report token usage via the callback if set."""
96
+ if (callback := _token_callback.get()) is not None:
97
+ callback(usage)
98
+
99
+
100
+ def set_output_dir(output_dir: Path | None) -> None:
101
+ """Set the output directory for internal tools."""
102
+ _output_dir.set(output_dir)
103
+
104
+
105
+ def get_output_dir() -> Path:
106
+ """Get the output directory for internal tools."""
107
+ if (output_dir := _output_dir.get()) is not None:
108
+ return output_dir
109
+ fallback = Path("./outputs")
110
+ fallback.mkdir(exist_ok=True)
111
+ return fallback
112
+
113
+
114
+ def set_mcp_connection(connection: MCPConnection | None, loop: asyncio.AbstractEventLoop | None) -> None:
115
+ """Set the MCP connection for use by internal tools (pass None to clear)."""
116
+ _mcp_connection.set(connection)
117
+ _mcp_event_loop.set(loop)
118
+
119
+
120
+ def get_mcp_connection() -> MCPConnection | None:
121
+ """Get the current MCP connection."""
122
+ return _mcp_connection.get()
123
+
124
+
125
+ def get_mcp_event_loop() -> asyncio.AbstractEventLoop | None:
126
+ """Get the current MCP event loop."""
127
+ return _mcp_event_loop.get()
128
+
129
+
130
+ def set_rossum_credentials(api_base_url: str | None, token: str | None) -> None:
131
+ """Set Rossum API credentials for internal tools.
132
+
133
+ Args:
134
+ api_base_url: Rossum API base URL.
135
+ token: Rossum API token.
136
+ """
137
+ if api_base_url and token:
138
+ _rossum_credentials.set((api_base_url, token))
139
+ else:
140
+ _rossum_credentials.set(None)
141
+
142
+
143
+ def get_rossum_credentials() -> tuple[str, str] | None:
144
+ """Get Rossum API credentials from context or environment.
145
+
146
+ Checks context first (set by API service), then falls back to environment variables.
147
+
148
+ Returns:
149
+ Tuple of (api_base_url, token) or None if neither context nor env vars are set.
150
+ """
151
+ if (creds := _rossum_credentials.get()) is not None:
152
+ return creds
153
+
154
+ api_base = os.getenv("ROSSUM_API_BASE_URL")
155
+ token = os.getenv("ROSSUM_API_TOKEN")
156
+ if api_base and token:
157
+ return api_base, token
158
+
159
+ return None
160
+
161
+
162
+ def require_rossum_credentials() -> tuple[str, str]:
163
+ """Get Rossum API credentials, raising if unavailable.
164
+
165
+ Returns:
166
+ Tuple of (api_base_url, token).
167
+
168
+ Raises:
169
+ ValueError: If credentials are not available.
170
+ """
171
+ if (creds := get_rossum_credentials()) is not None:
172
+ return creds
173
+ raise ValueError("Rossum API credentials not available (neither in context nor environment variables)")
@@ -0,0 +1,404 @@
1
+ """Deployment tools for the Rossum Agent.
2
+
3
+ This module provides tools for managing Rossum configuration deployments,
4
+ including pull, diff, push, and cross-org copy operations using rossum-deploy.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING
13
+
14
+ from anthropic import beta_tool
15
+ from rossum_deploy.models import IdMapping
16
+ from rossum_deploy.workspace import Workspace
17
+
18
+ from rossum_agent.tools.core import get_output_dir, require_rossum_credentials
19
+
20
+ if TYPE_CHECKING:
21
+ from anthropic._tools import BetaTool # ty: ignore[unresolved-import] - private API
22
+ from anthropic.types import ToolParam
23
+ from rossum_deploy.workspace import Workspace as WorkspaceType
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ def create_workspace(
29
+ path: str | None = None, api_base_url: str | None = None, token: str | None = None
30
+ ) -> WorkspaceType:
31
+ """Create a Workspace instance for deployment operations."""
32
+ default_api_base, default_token = require_rossum_credentials()
33
+ api_base = api_base_url or default_api_base
34
+ api_token = token or default_token
35
+
36
+ workspace_path = Path(path) if path else get_output_dir() / "rossum-config"
37
+ workspace_path.mkdir(parents=True, exist_ok=True)
38
+
39
+ return Workspace(workspace_path, api_base=api_base, token=api_token)
40
+
41
+
42
+ @beta_tool
43
+ def deploy_pull(
44
+ org_id: int, workspace_path: str | None = None, api_base_url: str | None = None, token: str | None = None
45
+ ) -> str:
46
+ """Pull Rossum configuration objects from an organization to local files.
47
+
48
+ Downloads workspaces, queues, schemas, hooks, inboxes, and other objects
49
+ to local JSON files for version control and deployment workflows.
50
+
51
+ Args:
52
+ org_id: The organization ID to pull from.
53
+ workspace_path: Optional path to the workspace directory.
54
+ Defaults to './rossum-config' in the session output directory.
55
+ api_base_url: Optional API base URL for the target environment.
56
+ Use this when pulling from sandbox/different environment.
57
+ token: Optional API token for the target environment.
58
+ Use this when pulling from sandbox/different environment.
59
+
60
+ Returns:
61
+ JSON with pull summary including counts of pulled objects.
62
+ """
63
+ logger.info(f"deploy_pull called with {org_id=}, {workspace_path=}, {api_base_url=}")
64
+
65
+ try:
66
+ ws = create_workspace(workspace_path, api_base_url=api_base_url, token=token)
67
+ result = ws.pull(org_id=org_id)
68
+
69
+ return json.dumps(
70
+ {
71
+ "status": "success",
72
+ "summary": result.summary(),
73
+ "pulled_count": len(result.pulled),
74
+ "skipped_count": len(result.skipped),
75
+ "workspace_path": str(ws.path),
76
+ }
77
+ )
78
+ except Exception as e:
79
+ logger.exception("Error in deploy_pull")
80
+ return json.dumps({"status": "error", "error": str(e)})
81
+
82
+
83
+ @beta_tool
84
+ def deploy_diff(workspace_path: str | None = None) -> str:
85
+ """Compare local workspace files with remote Rossum configuration.
86
+
87
+ Shows which objects have been modified locally, remotely, or have conflicts.
88
+
89
+ Args:
90
+ workspace_path: Optional path to the workspace directory.
91
+ Defaults to './rossum-config' in the session output directory.
92
+
93
+ Returns:
94
+ JSON with diff summary showing unchanged, modified, and conflicting objects.
95
+ """
96
+ logger.info(f"deploy_diff called with {workspace_path=}")
97
+
98
+ try:
99
+ ws = create_workspace(workspace_path)
100
+ result = ws.diff()
101
+
102
+ return json.dumps(
103
+ {
104
+ "status": "success",
105
+ "summary": result.summary(),
106
+ "unchanged": result.total_unchanged,
107
+ "local_modified": result.total_local_modified,
108
+ "remote_modified": result.total_remote_modified,
109
+ "conflicts": result.total_conflicts,
110
+ "workspace_path": str(ws.path),
111
+ }
112
+ )
113
+ except Exception as e:
114
+ logger.exception("Error in deploy_diff")
115
+ return json.dumps({"status": "error", "error": str(e)})
116
+
117
+
118
+ @beta_tool
119
+ def deploy_push(dry_run: bool = False, force: bool = False, workspace_path: str | None = None) -> str:
120
+ """Push local changes to Rossum.
121
+
122
+ Uploads modified local configuration to the remote Rossum organization.
123
+
124
+ Args:
125
+ dry_run: If True, only show what would be pushed without making changes.
126
+ force: If True, push even if there are conflicts.
127
+ workspace_path: Optional path to the workspace directory.
128
+ Defaults to './rossum-config' in the session output directory.
129
+
130
+ Returns:
131
+ JSON with push summary including counts of pushed, skipped, and failed objects.
132
+ """
133
+ logger.info(f"deploy_push called with {dry_run=}, {force=}, {workspace_path=}")
134
+
135
+ try:
136
+ ws = create_workspace(workspace_path)
137
+
138
+ if dry_run:
139
+ result = ws.push(dry_run=True)
140
+ return json.dumps(
141
+ {
142
+ "status": "success",
143
+ "dry_run": True,
144
+ "summary": result.summary(),
145
+ "would_push_count": len(result.pushed),
146
+ "would_skip_count": len(result.skipped),
147
+ "workspace_path": str(ws.path),
148
+ }
149
+ )
150
+
151
+ result = ws.push(force=force)
152
+ return json.dumps(
153
+ {
154
+ "status": "success",
155
+ "dry_run": False,
156
+ "summary": result.summary(),
157
+ "pushed_count": len(result.pushed),
158
+ "skipped_count": len(result.skipped),
159
+ "failed_count": len(result.failed),
160
+ "workspace_path": str(ws.path),
161
+ }
162
+ )
163
+ except Exception as e:
164
+ logger.exception("Error in deploy_push")
165
+ return json.dumps({"status": "error", "error": str(e)})
166
+
167
+
168
+ @beta_tool
169
+ def deploy_copy_org(
170
+ source_org_id: int,
171
+ target_org_id: int,
172
+ target_api_base: str | None = None,
173
+ target_token: str | None = None,
174
+ workspace_path: str | None = None,
175
+ ) -> str:
176
+ """Copy all objects from source organization to target organization.
177
+
178
+ Creates copies of all workspaces, queues, schemas, hooks, and other objects
179
+ from source org to target org. Saves ID mappings for later deployment.
180
+
181
+ Use this to mirror production to sandbox before making changes.
182
+
183
+ Args:
184
+ source_org_id: Source organization ID (e.g., production).
185
+ target_org_id: Target organization ID (e.g., sandbox).
186
+ target_api_base: Target API base URL if different from source.
187
+ target_token: Target API token if different from source.
188
+ workspace_path: Optional path to the workspace directory.
189
+ Defaults to './rossum-config' in the session output directory.
190
+
191
+ Returns:
192
+ JSON with copy summary including counts of created, skipped, and failed objects.
193
+ """
194
+ logger.info(
195
+ f"deploy_copy_org called with {source_org_id=}, {target_org_id=}, {target_api_base=}, {workspace_path=}"
196
+ )
197
+
198
+ try:
199
+ ws = create_workspace(workspace_path)
200
+
201
+ result = ws.copy_org(
202
+ source_org_id=source_org_id,
203
+ target_org_id=target_org_id,
204
+ target_api_base=target_api_base,
205
+ target_token=target_token,
206
+ )
207
+
208
+ return json.dumps(
209
+ {
210
+ "status": "success",
211
+ "summary": result.summary(),
212
+ "created_count": len(result.created),
213
+ "skipped_count": len(result.skipped),
214
+ "failed_count": len(result.failed),
215
+ "workspace_path": str(ws.path),
216
+ }
217
+ )
218
+ except Exception as e:
219
+ logger.exception("Error in deploy_copy_org")
220
+ return json.dumps({"status": "error", "error": str(e)})
221
+
222
+
223
+ @beta_tool
224
+ def deploy_copy_workspace(
225
+ source_workspace_id: int,
226
+ target_org_id: int,
227
+ target_api_base: str | None = None,
228
+ target_token: str | None = None,
229
+ workspace_path: str | None = None,
230
+ ) -> str:
231
+ """Copy a single workspace and all its objects to target organization.
232
+
233
+ Copies a workspace with all its queues, schemas, engines, hooks, connectors,
234
+ inboxes, email templates, and rules to the target organization.
235
+
236
+ Useful when you only need to replicate part of an org rather than the entire organization.
237
+
238
+ Args:
239
+ source_workspace_id: Source workspace ID to copy.
240
+ target_org_id: Target organization ID to copy to.
241
+ target_api_base: Target API base URL if different from source.
242
+ target_token: Target API token if different from source.
243
+ workspace_path: Optional path to the workspace directory.
244
+ Defaults to './rossum-config' in the session output directory.
245
+
246
+ Returns:
247
+ JSON with copy summary including counts of created, skipped, and failed objects.
248
+ """
249
+ logger.info(
250
+ f"deploy_copy_workspace called with {source_workspace_id=}, {target_org_id=}, {target_api_base=}, {workspace_path=}"
251
+ )
252
+
253
+ try:
254
+ ws = create_workspace(workspace_path)
255
+
256
+ result = ws.copy_workspace(
257
+ source_workspace_id=source_workspace_id,
258
+ target_org_id=target_org_id,
259
+ target_api_base=target_api_base,
260
+ target_token=target_token,
261
+ )
262
+
263
+ return json.dumps(
264
+ {
265
+ "status": "success",
266
+ "summary": result.summary(),
267
+ "created_count": len(result.created),
268
+ "skipped_count": len(result.skipped),
269
+ "failed_count": len(result.failed),
270
+ "workspace_path": str(ws.path),
271
+ }
272
+ )
273
+ except Exception as e:
274
+ logger.exception("Error in deploy_copy_workspace")
275
+ return json.dumps({"status": "error", "error": str(e)})
276
+
277
+
278
+ @beta_tool
279
+ def deploy_compare_workspaces(
280
+ source_workspace_path: str, target_workspace_path: str, id_mapping_path: str | None = None
281
+ ) -> str:
282
+ """Compare two local workspaces to see differences between source and target.
283
+
284
+ PREREQUISITE: Both directories must contain JSON files from deploy_pull.
285
+ This tool compares local files only - it does not fetch from remote APIs.
286
+ Call deploy_pull twice (before and after modifications) before using this tool.
287
+
288
+ Two use cases:
289
+ 1. Compare prod vs sandbox: Pass id_mapping_path from copy_workspace to map IDs
290
+ 2. Compare before vs after: Pass id_mapping_path=None to compare same workspace
291
+
292
+ Args:
293
+ source_workspace_path: Path to the source (original/production) workspace directory.
294
+ Must contain JSON files from deploy_pull.
295
+ target_workspace_path: Path to the target (modified/sandbox) workspace directory.
296
+ Must contain JSON files from deploy_pull.
297
+ id_mapping_path: Optional path to ID mapping JSON file from copy_workspace.
298
+ If None, objects are matched by their original IDs.
299
+
300
+ Returns:
301
+ JSON with comparison summary showing identical, different, source-only,
302
+ and target-only objects with field-level diffs.
303
+ """
304
+ logger.info(
305
+ f"deploy_compare_workspaces called with {source_workspace_path=}, {target_workspace_path=}, {id_mapping_path=}"
306
+ )
307
+
308
+ try:
309
+ api_base, token = require_rossum_credentials()
310
+
311
+ source_ws = Workspace(Path(source_workspace_path), api_base=api_base, token=token)
312
+ target_ws = Workspace(Path(target_workspace_path), api_base=api_base, token=token)
313
+
314
+ id_mapping = None
315
+ if id_mapping_path:
316
+ with open(id_mapping_path) as f:
317
+ id_mapping = IdMapping.model_validate(json.load(f))
318
+
319
+ result = source_ws.compare_workspaces(target_ws, id_mapping=id_mapping)
320
+
321
+ return json.dumps(
322
+ {
323
+ "status": "success",
324
+ "summary": result.summary(color=False),
325
+ "source_workspace_id": result.source_workspace_id,
326
+ "target_workspace_id": result.target_workspace_id,
327
+ "total_identical": result.total_identical,
328
+ "total_different": result.total_different,
329
+ "source_only_count": len(result.source_only),
330
+ "target_only_count": len(result.target_only),
331
+ }
332
+ )
333
+ except Exception as e:
334
+ logger.exception("Error in deploy_compare_workspaces")
335
+ return json.dumps({"status": "error", "error": str(e)})
336
+
337
+
338
+ @beta_tool
339
+ def deploy_to_org(
340
+ target_org_id: int,
341
+ target_api_base: str | None = None,
342
+ target_token: str | None = None,
343
+ dry_run: bool = False,
344
+ workspace_path: str | None = None,
345
+ ) -> str:
346
+ """Deploy local configuration changes to a target organization.
347
+
348
+ Uses saved ID mappings from copy_org to update the corresponding objects
349
+ in the target organization. This is the final step in the deployment workflow.
350
+
351
+ Args:
352
+ target_org_id: Target organization ID to deploy to.
353
+ target_api_base: Target API base URL if different from source.
354
+ target_token: Target API token if different from source.
355
+ dry_run: If True, only show what would be deployed without making changes.
356
+ workspace_path: Optional path to the workspace directory.
357
+ Defaults to './rossum-config' in the session output directory.
358
+
359
+ Returns:
360
+ JSON with deploy summary including counts of created, updated, skipped, and failed objects.
361
+ """
362
+ logger.info(f"deploy_to_org called with {target_org_id=}, {target_api_base=}, {dry_run=}, {workspace_path=}")
363
+
364
+ try:
365
+ ws = create_workspace(workspace_path)
366
+
367
+ result = ws.deploy(
368
+ target_org_id=target_org_id, target_api_base=target_api_base, target_token=target_token, dry_run=dry_run
369
+ )
370
+
371
+ return json.dumps(
372
+ {
373
+ "status": "success",
374
+ "dry_run": dry_run,
375
+ "summary": result.summary(),
376
+ "created_count": len(result.created),
377
+ "updated_count": len(result.updated),
378
+ "skipped_count": len(result.skipped),
379
+ "failed_count": len(result.failed),
380
+ "workspace_path": str(ws.path),
381
+ }
382
+ )
383
+ except Exception as e:
384
+ logger.exception("Error in deploy_to_org")
385
+ return json.dumps({"status": "error", "error": str(e)})
386
+
387
+
388
+ DEPLOY_TOOLS: list[BetaTool[..., str]] = [
389
+ deploy_pull,
390
+ deploy_diff,
391
+ deploy_push,
392
+ deploy_copy_org,
393
+ deploy_copy_workspace,
394
+ deploy_compare_workspaces,
395
+ deploy_to_org,
396
+ ]
397
+
398
+
399
+ def get_deploy_tools() -> list[ToolParam]:
400
+ return [tool.to_dict() for tool in DEPLOY_TOOLS]
401
+
402
+
403
+ def get_deploy_tool_names() -> set[str]:
404
+ return {tool.name for tool in DEPLOY_TOOLS}