mainsequence 4.4.22__tar.gz → 4.4.24__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.
Files changed (161) hide show
  1. {mainsequence-4.4.22/mainsequence.egg-info → mainsequence-4.4.24}/PKG-INFO +1 -1
  2. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/a2a_communication/SKILL.md +149 -0
  3. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/api.py +4 -0
  4. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/cli.py +126 -5
  5. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/config.py +73 -0
  6. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/agent_runtime_models.py +51 -1
  7. {mainsequence-4.4.22 → mainsequence-4.4.24/mainsequence.egg-info}/PKG-INFO +1 -1
  8. {mainsequence-4.4.22 → mainsequence-4.4.24}/pyproject.toml +1 -1
  9. {mainsequence-4.4.22 → mainsequence-4.4.24}/LICENSE +0 -0
  10. {mainsequence-4.4.22 → mainsequence-4.4.24}/README.md +0 -0
  11. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/AGENTS.md +0 -0
  12. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  13. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
  14. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
  15. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
  16. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/command_center/widgets/app_components/SKILL.md +0 -0
  17. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/command_center/widgets/tables/SKILL.md +0 -0
  18. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/command_center/widgets/tabular_transform/SKILL.md +0 -0
  19. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
  20. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
  21. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
  22. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
  23. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  24. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
  25. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +0 -0
  26. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
  27. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  28. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
  29. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  30. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  31. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
  32. {mainsequence-4.4.22 → mainsequence-4.4.24}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
  33. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/__init__.py +0 -0
  34. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/__main__.py +0 -0
  35. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/bootstrap.py +0 -0
  36. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/__init__.py +0 -0
  37. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/browser_auth.py +0 -0
  38. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/docker_utils.py +0 -0
  39. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/doctor.py +0 -0
  40. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/local_ops.py +0 -0
  41. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/migrations.py +0 -0
  42. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/model_filters.py +0 -0
  43. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/project_status.py +0 -0
  44. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/pydantic_cli.py +0 -0
  45. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/sdk_utils.py +0 -0
  46. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/ssh_utils.py +0 -0
  47. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/cli/ui.py +0 -0
  48. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/__init__.py +0 -0
  49. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/base.py +0 -0
  50. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/client.py +0 -0
  51. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/__init__.py +0 -0
  52. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/app_component.py +0 -0
  53. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/connections.py +0 -0
  54. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/contracts/__init__.py +0 -0
  55. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/contracts/adapter_from_api.py +0 -0
  56. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/contracts/response_mapping.py +0 -0
  57. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/contracts/table_visuals.py +0 -0
  58. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/contracts/tabular.py +0 -0
  59. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/contracts/ui.py +0 -0
  60. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/data_models.py +0 -0
  61. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/providers/__init__.py +0 -0
  62. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/providers/adapter_from_api.py +0 -0
  63. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/widgets/__init__.py +0 -0
  64. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/widgets/bindings.py +0 -0
  65. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/widgets/connection_query.py +0 -0
  66. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/widgets/registry.py +0 -0
  67. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/widgets/table.py +0 -0
  68. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/widgets/tabular_transform.py +0 -0
  69. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/workspace.py +0 -0
  70. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
  71. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/workspaces/__init__.py +0 -0
  72. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/workspaces/documents.py +0 -0
  73. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/command_center/workspaces/mounted_widgets.py +0 -0
  74. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/compute_validation.py +0 -0
  75. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  76. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
  77. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
  78. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
  79. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/dtype_codec.py +0 -0
  80. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/exceptions.py +0 -0
  81. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/fastapi/__init__.py +0 -0
  82. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/fastapi/auth.py +0 -0
  83. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/metatables/__init__.py +0 -0
  84. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/metatables/core.py +0 -0
  85. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/models_foundry.py +0 -0
  86. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/models_helpers.py +0 -0
  87. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/models_user.py +0 -0
  88. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/client/utils.py +0 -0
  89. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/defaults.py +0 -0
  90. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/instrumentation/__init__.py +0 -0
  91. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/instrumentation/utils.py +0 -0
  92. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/logconf.py +0 -0
  93. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/__init__.py +0 -0
  94. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/__main__.py +0 -0
  95. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
  96. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
  97. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
  98. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
  99. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
  100. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/data_nodes/models.py +0 -0
  101. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
  102. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
  103. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
  104. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
  105. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/future_registry.py +0 -0
  106. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/hashing.py +0 -0
  107. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/migrations/__init__.py +0 -0
  108. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/migrations/alembic.py +0 -0
  109. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/migrations/env.py +0 -0
  110. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/migrations/provider.py +0 -0
  111. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/migrations/registry.py +0 -0
  112. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
  113. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
  114. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
  115. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
  116. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
  117. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/schema_names.py +0 -0
  118. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
  119. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/runtime_flags.py +0 -0
  120. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence/scaffold_skills.py +0 -0
  121. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence.egg-info/SOURCES.txt +0 -0
  122. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence.egg-info/dependency_links.txt +0 -0
  123. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence.egg-info/entry_points.txt +0 -0
  124. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence.egg-info/requires.txt +0 -0
  125. {mainsequence-4.4.22 → mainsequence-4.4.24}/mainsequence.egg-info/top_level.txt +0 -0
  126. {mainsequence-4.4.22 → mainsequence-4.4.24}/setup.cfg +0 -0
  127. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_auth_precedence.py +0 -0
  128. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_build_operations_hashing.py +0 -0
  129. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_cli.py +0 -0
  130. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_cli_browser_auth.py +0 -0
  131. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_cli_migrations.py +0 -0
  132. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_client.py +0 -0
  133. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_command_center_app_component_models.py +0 -0
  134. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_command_center_data_models.py +0 -0
  135. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_command_center_models.py +0 -0
  136. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_command_center_table_contracts.py +0 -0
  137. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_command_center_tabular_transform.py +0 -0
  138. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_data_access_mixin_dimension_audit.py +0 -0
  139. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_data_node_storage_dimension_queries.py +0 -0
  140. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_data_node_update_flow.py +0 -0
  141. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_dependency_extras.py +0 -0
  142. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_duckdb_interface_dimensions.py +0 -0
  143. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_filter_normalization.py +0 -0
  144. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_instrumentation.py +0 -0
  145. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_logconf.py +0 -0
  146. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_meta_table_migrations.py +0 -0
  147. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_meta_tables_client_models.py +0 -0
  148. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
  149. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_models_user_request_bound_auth.py +0 -0
  150. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_pod_project_resolution.py +0 -0
  151. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_project_batch_jobs_from_file.py +0 -0
  152. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_run_configuration.py +0 -0
  153. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_scaffold_skills.py +0 -0
  154. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_schema_names.py +0 -0
  155. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_secret_client_model.py +0 -0
  156. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_source_table_configuration.py +0 -0
  157. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_sqlite_interface_dimensions.py +0 -0
  158. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_update_runner_uid_runtime.py +0 -0
  159. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_update_statistics.py +0 -0
  160. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_update_uid_guards.py +0 -0
  161. {mainsequence-4.4.22 → mainsequence-4.4.24}/tests/test_workspace_snapshot.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 4.4.22
3
+ Version: 4.4.24
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License: MainSequence GmbH SDK License Agreement
@@ -105,6 +105,155 @@ mainsequence agent session a2a send \
105
105
  --message "Now identify the highest priority follow-up."
106
106
  ```
107
107
 
108
+ ## Handle Shortcut Helper
109
+
110
+ For repeated communication with the same target agent, use a stable handle. On
111
+ first use, include the target agent UID so the CLI can create or resolve the
112
+ target session and cache the returned session UID:
113
+
114
+ ```bash
115
+ mainsequence agent session a2a send \
116
+ <stable_handle_unique_id> \
117
+ --target-agent-uid <target_agent_uid> \
118
+ --message "Return a JSON object with keys: summary, risk_level, next_action." \
119
+ --strict-dictionary
120
+ ```
121
+
122
+ `--name` is optional and only affects the session if the backend creates it:
123
+
124
+ ```bash
125
+ mainsequence agent session a2a send \
126
+ portfolios \
127
+ --target-agent-uid <target_agent_uid> \
128
+ --name "Portfolio analysis" \
129
+ --message "Return a JSON object with keys: summary, risk_level, next_action." \
130
+ --strict-dictionary
131
+ ```
132
+
133
+ After the handle is cached, reuse it without the target agent UID:
134
+
135
+ ```bash
136
+ mainsequence agent session a2a send \
137
+ portfolios \
138
+ --message "Now identify the highest priority follow-up." \
139
+ --strict-dictionary
140
+ ```
141
+
142
+ ## Send Files
143
+
144
+ A2A can send inline files as raw parts. For PDFs, use `--file`; the CLI reads
145
+ the file bytes, enforces the 15 MiB inline limit, base64 encodes the bytes, and
146
+ sends the part with `filename` and `mediaType`.
147
+
148
+ ```bash
149
+ mainsequence agent session a2a send \
150
+ <target_agent_session_uid> \
151
+ --message "Please summarize this PDF." \
152
+ --file report.pdf \
153
+ --media-type application/pdf
154
+ ```
155
+
156
+ The same handle shortcut works with files:
157
+
158
+ ```bash
159
+ mainsequence agent session a2a send \
160
+ portfolios \
161
+ --message "Please summarize this PDF." \
162
+ --file report.pdf \
163
+ --media-type application/pdf
164
+ ```
165
+
166
+ For retries of the exact same message and file attachment, reuse the same
167
+ `--message-id`.
168
+
169
+ ## Python Usage
170
+
171
+ In Python, use the backend handle to get or create the target session, then
172
+ reuse the returned session UID for every message in the same conversation. The
173
+ binding is:
174
+
175
+ ```text
176
+ handle_unique_id -> AgentSession.uid -> A2A message.contextId
177
+ ```
178
+
179
+ Direct SDK flow:
180
+
181
+ ```python
182
+ from mainsequence.client.agent_runtime_models import Agent, AgentSession
183
+
184
+
185
+ agent = Agent.get_by_uid(target_agent_uid)
186
+ session = agent.get_or_create_session(
187
+ handle_unique_id="portfolios",
188
+ name="Portfolio analysis",
189
+ )
190
+
191
+ response = AgentSession.send_a2a_message(
192
+ session.uid,
193
+ message="Return a JSON object with keys: summary, risk_level, next_action.",
194
+ strict_dictionary=True,
195
+ )
196
+ ```
197
+
198
+ For another message in the same conversation, reuse the same `session.uid`:
199
+
200
+ ```python
201
+ response = AgentSession.send_a2a_message(
202
+ session.uid,
203
+ message="Now identify the highest priority follow-up.",
204
+ strict_dictionary=True,
205
+ )
206
+ ```
207
+
208
+ To send a PDF, pass a file attachment. The SDK reads the raw bytes, enforces the
209
+ 15 MiB inline limit, base64 encodes them, and sends `filename` plus `mediaType`:
210
+
211
+ ```python
212
+ response = AgentSession.send_a2a_message(
213
+ session.uid,
214
+ message="Please summarize this PDF.",
215
+ files=[
216
+ {
217
+ "path": "report.pdf",
218
+ "media_type": "application/pdf",
219
+ "filename": "report.pdf",
220
+ }
221
+ ],
222
+ )
223
+ ```
224
+
225
+ Reusable helper:
226
+
227
+ ```python
228
+ from mainsequence.client.agent_runtime_models import Agent, AgentSession
229
+
230
+
231
+ def send_to_agent_handle(
232
+ target_agent_uid: str,
233
+ handle_unique_id: str,
234
+ message: str,
235
+ *,
236
+ name: str | None = None,
237
+ files: list[dict[str, str]] | None = None,
238
+ strict_dictionary: bool = False,
239
+ ):
240
+ agent = Agent.get_by_uid(target_agent_uid)
241
+ session = agent.get_or_create_session(
242
+ handle_unique_id=handle_unique_id,
243
+ name=name,
244
+ )
245
+
246
+ return AgentSession.send_a2a_message(
247
+ session.uid, # The SDK sends this as A2A message.contextId.
248
+ message=message,
249
+ files=files,
250
+ strict_dictionary=strict_dictionary,
251
+ )
252
+ ```
253
+
254
+ Use the same `handle_unique_id` for the same target conversation. Use a new
255
+ handle for a different task or conversation.
256
+
108
257
  ## Response Handling
109
258
 
110
259
  - Parse the CLI output as the standard A2A JSON response.
@@ -1244,6 +1244,7 @@ def send_agent_session_a2a_message(
1244
1244
  agent_session_uid: str,
1245
1245
  *,
1246
1246
  message: str,
1247
+ files: list[dict[str, Any]] | None = None,
1247
1248
  message_id: str | None = None,
1248
1249
  strict_dictionary: bool = False,
1249
1250
  json_repair_attempts: int = 3,
@@ -1262,6 +1263,7 @@ def send_agent_session_a2a_message(
1262
1263
  ClientAgentSession,
1263
1264
  agent_session_uid=str(agent_session_uid),
1264
1265
  message=message,
1266
+ files=files,
1265
1267
  message_id=message_id,
1266
1268
  cached_runtime_access=cached_runtime_access,
1267
1269
  strict_dictionary=strict_dictionary,
@@ -1287,6 +1289,7 @@ def _send_agent_session_a2a_message_with_cache(
1287
1289
  *,
1288
1290
  agent_session_uid: str,
1289
1291
  message: str,
1292
+ files: list[dict[str, Any]] | None,
1290
1293
  message_id: str | None,
1291
1294
  cached_runtime_access: dict[str, Any] | None,
1292
1295
  strict_dictionary: bool,
@@ -1300,6 +1303,7 @@ def _send_agent_session_a2a_message_with_cache(
1300
1303
  payload = ClientAgentSession.send_a2a_message(
1301
1304
  agent_session_uid,
1302
1305
  message=message,
1306
+ files=files,
1303
1307
  message_id=message_id,
1304
1308
  strict_dictionary=strict_dictionary,
1305
1309
  json_repair_attempts=json_repair_attempts,
@@ -3829,11 +3829,77 @@ def _extract_standard_a2a_message_text(payload: dict[str, object]) -> str:
3829
3829
  return "".join(chunks)
3830
3830
 
3831
3831
 
3832
+ def _looks_like_uuid(value: str) -> bool:
3833
+ try:
3834
+ uuid.UUID(str(value))
3835
+ return True
3836
+ except (TypeError, ValueError):
3837
+ return False
3838
+
3839
+
3840
+ def _resolve_agent_session_uid_or_handle(
3841
+ target: str,
3842
+ *,
3843
+ target_agent_uid: str | None,
3844
+ name: str | None,
3845
+ parent_session_uid: str | None,
3846
+ timeout: int | None,
3847
+ ) -> str:
3848
+ resolved_target = str(target or "").strip()
3849
+ if not resolved_target:
3850
+ error("agent session UID or handle is required.")
3851
+ raise typer.Exit(1)
3852
+ if _looks_like_uuid(resolved_target):
3853
+ return resolved_target
3854
+
3855
+ resolved_agent_uid = str(target_agent_uid or "").strip()
3856
+ if not resolved_agent_uid:
3857
+ cached = cfg.get_a2a_handle_cache(resolved_target)
3858
+ cached_session_uid = str((cached or {}).get("agent_session_uid") or "").strip()
3859
+ if cached_session_uid:
3860
+ return cached_session_uid
3861
+ error(
3862
+ f"No cached A2A session for handle '{resolved_target}'. "
3863
+ "Pass --target-agent-uid once to create or resolve it."
3864
+ )
3865
+ raise typer.Exit(1)
3866
+
3867
+ try:
3868
+ agent_session_payload = get_or_create_agent_session(
3869
+ resolved_agent_uid,
3870
+ handle_unique_id=resolved_target,
3871
+ name=name,
3872
+ parent_session_uid=parent_session_uid,
3873
+ timeout=timeout,
3874
+ )
3875
+ except ApiError as e:
3876
+ error(f"Agent session handle resolution failed: {e}")
3877
+ raise typer.Exit(1) from e
3878
+
3879
+ agent_session_uid = str(agent_session_payload.get("uid") or "").strip()
3880
+ if not agent_session_uid:
3881
+ error("Agent session handle resolution did not return a session UID.")
3882
+ raise typer.Exit(1)
3883
+
3884
+ cfg.save_a2a_handle_cache(
3885
+ resolved_target,
3886
+ agent_uid=resolved_agent_uid,
3887
+ agent_session_uid=agent_session_uid,
3888
+ name=name,
3889
+ )
3890
+ return agent_session_uid
3891
+
3892
+
3832
3893
  def _agent_session_a2a_send_impl(
3833
3894
  *,
3834
- agent_session_uid: str,
3895
+ agent_session_uid_or_handle: str,
3896
+ target_agent_uid: str | None,
3897
+ name: str | None,
3898
+ parent_session_uid: str | None,
3835
3899
  message: str | None,
3836
3900
  message_file: pathlib.Path | None,
3901
+ files: list[pathlib.Path] | None,
3902
+ media_types: list[str] | None,
3837
3903
  strict_dictionary: bool,
3838
3904
  json_repair_attempts: int,
3839
3905
  message_id: str | None,
@@ -3855,16 +3921,38 @@ def _agent_session_a2a_send_impl(
3855
3921
  if json_repair_attempts < 1:
3856
3922
  error("--json-repair-attempts must be greater than 0.")
3857
3923
  raise typer.Exit(1)
3924
+ attachment_paths = list(files or [])
3925
+ attachment_media_types = list(media_types or [])
3926
+ if attachment_media_types and len(attachment_media_types) != len(attachment_paths):
3927
+ error("--media-type must be provided once per --file when used.")
3928
+ raise typer.Exit(1)
3929
+ attachments = [
3930
+ {
3931
+ "path": str(path),
3932
+ "media_type": attachment_media_types[index]
3933
+ if attachment_media_types
3934
+ else "application/pdf",
3935
+ }
3936
+ for index, path in enumerate(attachment_paths)
3937
+ ]
3858
3938
  effective_message_id = str(message_id).strip() if message_id is not None else ""
3859
3939
  if not effective_message_id:
3860
3940
  effective_message_id = f"msg-{uuid.uuid4()}"
3861
3941
 
3862
3942
  _require_login()
3943
+ agent_session_uid = _resolve_agent_session_uid_or_handle(
3944
+ agent_session_uid_or_handle,
3945
+ target_agent_uid=target_agent_uid,
3946
+ name=name,
3947
+ parent_session_uid=parent_session_uid,
3948
+ timeout=timeout,
3949
+ )
3863
3950
 
3864
3951
  try:
3865
3952
  response_payload = send_agent_session_a2a_message(
3866
3953
  agent_session_uid,
3867
3954
  message=resolved_message,
3955
+ files=attachments,
3868
3956
  message_id=effective_message_id,
3869
3957
  strict_dictionary=strict_dictionary,
3870
3958
  json_repair_attempts=json_repair_attempts,
@@ -6677,8 +6765,26 @@ def agent_session_get_or_create_cmd(
6677
6765
 
6678
6766
  @agent_session_a2a_group.command("send")
6679
6767
  def agent_session_a2a_send_cmd(
6680
- agent_session_uid: str = pydantic_argument(
6681
- AGENT_SESSION_MODEL_REF, "uid", ..., help="Agent session UID."
6768
+ agent_session_uid_or_handle: str = pydantic_argument(
6769
+ AGENT_SESSION_MODEL_REF,
6770
+ "uid",
6771
+ ...,
6772
+ help="Agent session UID, or cached A2A handle.",
6773
+ ),
6774
+ target_agent_uid: str | None = typer.Option(
6775
+ None,
6776
+ "--target-agent-uid",
6777
+ help="Target agent UID used to create or resolve a handle on first use.",
6778
+ ),
6779
+ name: str | None = typer.Option(
6780
+ None,
6781
+ "--name",
6782
+ help="Optional session name used only when resolving a handle with --target-agent-uid.",
6783
+ ),
6784
+ parent_session_uid: str | None = typer.Option(
6785
+ None,
6786
+ "--parent-session-uid",
6787
+ help="Optional parent session UID used only when resolving a handle with --target-agent-uid.",
6682
6788
  ),
6683
6789
  message: str | None = typer.Option(
6684
6790
  None,
@@ -6690,6 +6796,16 @@ def agent_session_a2a_send_cmd(
6690
6796
  "--message-file",
6691
6797
  help="Path to a UTF-8 text file containing the A2A message.",
6692
6798
  ),
6799
+ files: list[pathlib.Path] | None = typer.Option(
6800
+ None,
6801
+ "--file",
6802
+ help="Inline file attachment to send as an A2A raw part.",
6803
+ ),
6804
+ media_types: list[str] | None = typer.Option(
6805
+ None,
6806
+ "--media-type",
6807
+ help="Media type for each --file. Defaults to application/pdf.",
6808
+ ),
6693
6809
  strict_dictionary: bool = typer.Option(
6694
6810
  False,
6695
6811
  "--strict-dictionary",
@@ -6713,12 +6829,17 @@ def agent_session_a2a_send_cmd(
6713
6829
  timeout: int | None = typer.Option(None, "--timeout", help="Request timeout in seconds"),
6714
6830
  ):
6715
6831
  """
6716
- Send one standard A2A message to an existing agent session.
6832
+ Send one standard A2A message to an existing session UID or cached handle.
6717
6833
  """
6718
6834
  _agent_session_a2a_send_impl(
6719
- agent_session_uid=agent_session_uid,
6835
+ agent_session_uid_or_handle=agent_session_uid_or_handle,
6836
+ target_agent_uid=target_agent_uid,
6837
+ name=name,
6838
+ parent_session_uid=parent_session_uid,
6720
6839
  message=message,
6721
6840
  message_file=message_file,
6841
+ files=files,
6842
+ media_types=media_types,
6722
6843
  strict_dictionary=strict_dictionary,
6723
6844
  json_repair_attempts=json_repair_attempts,
6724
6845
  message_id=message_id,
@@ -55,6 +55,7 @@ SESSION_OVERRIDES_DIR = CFG_DIR / "session_overrides"
55
55
  TOKENS_JSON = CFG_DIR / "token.json"
56
56
  AUTH_JSON = CFG_DIR / "auth.json"
57
57
  RUNTIME_ACCESS_CACHE_JSON = CFG_DIR / "runtime_access_cache.json"
58
+ A2A_HANDLE_CACHE_JSON = CFG_DIR / "a2a_handle_cache.json"
58
59
 
59
60
  # Session-scoped auth environment variables (no token file persistence).
60
61
  ENV_USERNAME = "MAINSEQUENCE_USERNAME"
@@ -469,6 +470,78 @@ def clear_runtime_access_cache(
469
470
  return False
470
471
 
471
472
 
473
+ def _a2a_handle_cache_key(handle_unique_id: str, backend: str | None = None) -> str:
474
+ backend_key = _auth_backend_key(backend)
475
+ raw = f"{backend_key}|{_runtime_access_user_key()}|{str(handle_unique_id).strip()}"
476
+ return hashlib.sha256(raw.encode("utf-8")).hexdigest()
477
+
478
+
479
+ def _read_a2a_handle_cache() -> dict:
480
+ data = read_json(A2A_HANDLE_CACHE_JSON, {})
481
+ if not isinstance(data, dict):
482
+ return {"version": 1, "entries": {}}
483
+ entries = data.get("entries")
484
+ if not isinstance(entries, dict):
485
+ entries = {}
486
+ return {"version": 1, "entries": entries}
487
+
488
+
489
+ def _write_a2a_handle_cache(data: dict) -> None:
490
+ write_json(A2A_HANDLE_CACHE_JSON, data)
491
+ if os.name == "posix":
492
+ try:
493
+ os.chmod(A2A_HANDLE_CACHE_JSON, 0o600)
494
+ except Exception:
495
+ pass
496
+
497
+
498
+ def get_a2a_handle_cache(
499
+ handle_unique_id: str,
500
+ *,
501
+ backend: str | None = None,
502
+ ) -> dict | None:
503
+ """
504
+ Return a cached A2A handle mapping scoped by backend and current user.
505
+ """
506
+ handle = str(handle_unique_id or "").strip()
507
+ if not handle:
508
+ return None
509
+ cache = _read_a2a_handle_cache()
510
+ entry = cache["entries"].get(_a2a_handle_cache_key(handle, backend))
511
+ return dict(entry) if isinstance(entry, dict) else None
512
+
513
+
514
+ def save_a2a_handle_cache(
515
+ handle_unique_id: str,
516
+ *,
517
+ agent_uid: str,
518
+ agent_session_uid: str,
519
+ name: str | None = None,
520
+ backend: str | None = None,
521
+ ) -> dict:
522
+ """
523
+ Cache an A2A handle to the backend AgentSession UID it resolves to.
524
+ """
525
+ handle = str(handle_unique_id or "").strip()
526
+ if not handle:
527
+ raise ValueError("handle_unique_id is required")
528
+
529
+ now = time.time()
530
+ entry = {
531
+ "backend_url": _auth_backend_key(backend),
532
+ "handle_unique_id": handle,
533
+ "agent_uid": str(agent_uid),
534
+ "agent_session_uid": str(agent_session_uid),
535
+ "name": str(name) if name is not None else None,
536
+ "cached_at_epoch": now,
537
+ "cached_at": _iso_utc_from_epoch(now),
538
+ }
539
+ cache = _read_a2a_handle_cache()
540
+ cache["entries"][_a2a_handle_cache_key(handle, backend)] = entry
541
+ _write_a2a_handle_cache(cache)
542
+ return dict(entry)
543
+
544
+
472
545
  def _keychain_account_for_backend(backend: str | None = None) -> str:
473
546
  """
474
547
  Return the backend-scoped keychain account name.
@@ -1,7 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import base64
3
4
  import datetime
4
5
  import json
6
+ import pathlib
5
7
  import time
6
8
  import uuid
7
9
  from enum import Enum
@@ -22,6 +24,7 @@ STANDARD_A2A_CONTENT_TYPE = "application/a2a+json"
22
24
  STANDARD_A2A_OUTPUT_CONTRACT_METADATA_KEY = (
23
25
  "https://mainsequence.ai/a2a/extensions/output-contract/v1"
24
26
  )
27
+ MAX_INLINE_A2A_FILE_BYTES = 15 * 1024 * 1024
25
28
 
26
29
 
27
30
  class AgentSessionStatus(str, Enum):
@@ -712,6 +715,7 @@ class AgentSession(BaseObjectOrm, BasePydanticModel):
712
715
  *,
713
716
  agent_session_uid: str,
714
717
  message: str,
718
+ files: list[Any] | None = None,
715
719
  message_id: str | None = None,
716
720
  strict_dictionary: bool = False,
717
721
  json_repair_attempts: int = 3,
@@ -727,12 +731,16 @@ class AgentSession(BaseObjectOrm, BasePydanticModel):
727
731
  normalized_message_id = f"msg-{uuid.uuid4()}"
728
732
 
729
733
  accepted_output_modes = ["application/json"] if strict_dictionary else ["text/plain"]
734
+ parts: list[dict[str, Any]] = [{"text": normalized_message}]
735
+ for file_spec in files or []:
736
+ parts.append(AgentSession._build_standard_a2a_raw_file_part(file_spec))
737
+
730
738
  body: dict[str, Any] = {
731
739
  "message": {
732
740
  "messageId": normalized_message_id,
733
741
  "role": "ROLE_USER",
734
742
  "contextId": agent_session_uid,
735
- "parts": [{"text": normalized_message}],
743
+ "parts": parts,
736
744
  },
737
745
  "configuration": {
738
746
  "acceptedOutputModes": accepted_output_modes,
@@ -751,6 +759,46 @@ class AgentSession(BaseObjectOrm, BasePydanticModel):
751
759
  }
752
760
  return body
753
761
 
762
+ @staticmethod
763
+ def _build_standard_a2a_raw_file_part(file_spec: Any) -> dict[str, Any]:
764
+ if isinstance(file_spec, (str, pathlib.Path)):
765
+ path = pathlib.Path(file_spec).expanduser()
766
+ media_type = "application/pdf" if path.suffix.lower() == ".pdf" else ""
767
+ filename = path.name
768
+ elif isinstance(file_spec, dict):
769
+ raw_path = file_spec.get("path")
770
+ if raw_path is None:
771
+ raise ValueError("A2A file attachment requires a path")
772
+ path = pathlib.Path(str(raw_path)).expanduser()
773
+ media_type = str(
774
+ file_spec.get("media_type") or file_spec.get("mediaType") or ""
775
+ ).strip()
776
+ if not media_type and path.suffix.lower() == ".pdf":
777
+ media_type = "application/pdf"
778
+ filename = str(file_spec.get("filename") or path.name).strip()
779
+ else:
780
+ raise TypeError("A2A file attachment must be a path or dict")
781
+
782
+ if not media_type:
783
+ raise ValueError(f"A2A file attachment '{path}' requires media_type")
784
+ if not filename:
785
+ raise ValueError(f"A2A file attachment '{path}' requires filename")
786
+
787
+ file_bytes = path.read_bytes()
788
+ if len(file_bytes) > MAX_INLINE_A2A_FILE_BYTES:
789
+ size_mib = len(file_bytes) / (1024 * 1024)
790
+ limit_mib = MAX_INLINE_A2A_FILE_BYTES / (1024 * 1024)
791
+ raise ValueError(
792
+ f"A2A attachment '{filename}' is {size_mib:.1f} MiB; "
793
+ f"inline attachments are limited to {limit_mib:.0f} MiB."
794
+ )
795
+
796
+ return {
797
+ "raw": base64.b64encode(file_bytes).decode("ascii"),
798
+ "filename": filename,
799
+ "mediaType": media_type,
800
+ }
801
+
754
802
  @classmethod
755
803
  def _post_standard_a2a_message(
756
804
  cls,
@@ -793,6 +841,7 @@ class AgentSession(BaseObjectOrm, BasePydanticModel):
793
841
  agent_session: str | AgentSession,
794
842
  *,
795
843
  message: str,
844
+ files: list[Any] | None = None,
796
845
  message_id: str | None = None,
797
846
  strict_dictionary: bool = False,
798
847
  json_repair_attempts: int = 3,
@@ -806,6 +855,7 @@ class AgentSession(BaseObjectOrm, BasePydanticModel):
806
855
  body = cls._build_standard_a2a_message_send_body(
807
856
  agent_session_uid=session_uid,
808
857
  message=message,
858
+ files=files,
809
859
  message_id=message_id,
810
860
  strict_dictionary=strict_dictionary,
811
861
  json_repair_attempts=json_repair_attempts,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 4.4.22
3
+ Version: 4.4.24
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License: MainSequence GmbH SDK License Agreement
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mainsequence"
7
- version = "4.4.22"
7
+ version = "4.4.24"
8
8
  description = "Main Sequence SDK "
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
File without changes
File without changes
File without changes