mainsequence 4.4.3__tar.gz → 4.4.5__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 (138) hide show
  1. {mainsequence-4.4.3/mainsequence.egg-info → mainsequence-4.4.5}/PKG-INFO +1 -1
  2. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/agent_runtime_models.py +87 -0
  3. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/data_nodes/persist_managers.py +2 -2
  4. {mainsequence-4.4.3 → mainsequence-4.4.5/mainsequence.egg-info}/PKG-INFO +1 -1
  5. {mainsequence-4.4.3 → mainsequence-4.4.5}/pyproject.toml +1 -1
  6. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_filter_normalization.py +107 -1
  7. {mainsequence-4.4.3 → mainsequence-4.4.5}/LICENSE +0 -0
  8. {mainsequence-4.4.3 → mainsequence-4.4.5}/README.md +0 -0
  9. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/AGENTS.md +0 -0
  10. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
  11. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  12. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
  13. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
  14. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
  15. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
  16. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
  17. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
  18. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
  19. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
  20. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  21. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
  22. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +0 -0
  23. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
  24. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  25. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
  26. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  27. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  28. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
  29. {mainsequence-4.4.3 → mainsequence-4.4.5}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
  30. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/__init__.py +0 -0
  31. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/__main__.py +0 -0
  32. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/bootstrap.py +0 -0
  33. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/__init__.py +0 -0
  34. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/api.py +0 -0
  35. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/browser_auth.py +0 -0
  36. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/cli.py +0 -0
  37. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/config.py +0 -0
  38. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/docker_utils.py +0 -0
  39. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/doctor.py +0 -0
  40. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/local_ops.py +0 -0
  41. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/migrations.py +0 -0
  42. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/model_filters.py +0 -0
  43. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/project_status.py +0 -0
  44. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/pydantic_cli.py +0 -0
  45. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/sdk_utils.py +0 -0
  46. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/ssh_utils.py +0 -0
  47. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/cli/ui.py +0 -0
  48. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/__init__.py +0 -0
  49. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/base.py +0 -0
  50. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/client.py +0 -0
  51. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/command_center/__init__.py +0 -0
  52. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/command_center/app_component.py +0 -0
  53. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/command_center/connections.py +0 -0
  54. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/command_center/data_models.py +0 -0
  55. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/command_center/workspace.py +0 -0
  56. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
  57. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/compute_validation.py +0 -0
  58. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  59. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
  60. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
  61. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
  62. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/dtype_codec.py +0 -0
  63. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/exceptions.py +0 -0
  64. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/fastapi/__init__.py +0 -0
  65. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/fastapi/auth.py +0 -0
  66. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/metatables/__init__.py +0 -0
  67. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/metatables/core.py +0 -0
  68. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/models_foundry.py +0 -0
  69. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/models_helpers.py +0 -0
  70. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/models_user.py +0 -0
  71. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/client/utils.py +0 -0
  72. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/defaults.py +0 -0
  73. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/instrumentation/__init__.py +0 -0
  74. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/instrumentation/utils.py +0 -0
  75. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/logconf.py +0 -0
  76. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/__init__.py +0 -0
  77. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/__main__.py +0 -0
  78. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
  79. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
  80. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
  81. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
  82. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
  83. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/data_nodes/models.py +0 -0
  84. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
  85. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
  86. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
  87. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/future_registry.py +0 -0
  88. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/hashing.py +0 -0
  89. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/migrations/__init__.py +0 -0
  90. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/migrations/alembic.py +0 -0
  91. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/migrations/env.py +0 -0
  92. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/migrations/provider.py +0 -0
  93. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/migrations/registry.py +0 -0
  94. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
  95. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
  96. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
  97. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
  98. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
  99. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/schema_names.py +0 -0
  100. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
  101. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence/runtime_flags.py +0 -0
  102. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence.egg-info/SOURCES.txt +0 -0
  103. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence.egg-info/dependency_links.txt +0 -0
  104. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence.egg-info/entry_points.txt +0 -0
  105. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence.egg-info/requires.txt +0 -0
  106. {mainsequence-4.4.3 → mainsequence-4.4.5}/mainsequence.egg-info/top_level.txt +0 -0
  107. {mainsequence-4.4.3 → mainsequence-4.4.5}/setup.cfg +0 -0
  108. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_auth_precedence.py +0 -0
  109. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_build_operations_hashing.py +0 -0
  110. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_cli.py +0 -0
  111. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_cli_browser_auth.py +0 -0
  112. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_cli_migrations.py +0 -0
  113. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_client.py +0 -0
  114. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_command_center_app_component_models.py +0 -0
  115. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_command_center_data_models.py +0 -0
  116. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_command_center_models.py +0 -0
  117. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_data_access_mixin_dimension_audit.py +0 -0
  118. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_data_node_storage_dimension_queries.py +0 -0
  119. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_data_node_update_flow.py +0 -0
  120. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_dependency_extras.py +0 -0
  121. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_duckdb_interface_dimensions.py +0 -0
  122. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_instrumentation.py +0 -0
  123. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_logconf.py +0 -0
  124. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_meta_table_migrations.py +0 -0
  125. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_meta_tables_client_models.py +0 -0
  126. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
  127. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_models_user_request_bound_auth.py +0 -0
  128. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_pod_project_resolution.py +0 -0
  129. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_project_batch_jobs_from_file.py +0 -0
  130. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_run_configuration.py +0 -0
  131. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_schema_names.py +0 -0
  132. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_secret_client_model.py +0 -0
  133. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_source_table_configuration.py +0 -0
  134. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_sqlite_interface_dimensions.py +0 -0
  135. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_update_runner_uid_runtime.py +0 -0
  136. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_update_statistics.py +0 -0
  137. {mainsequence-4.4.3 → mainsequence-4.4.5}/tests/test_update_uid_guards.py +0 -0
  138. {mainsequence-4.4.3 → mainsequence-4.4.5}/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.3
3
+ Version: 4.4.5
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
@@ -126,6 +126,18 @@ class Agent(ShareableObjectMixin, BaseObjectOrm, BasePydanticModel):
126
126
  None,
127
127
  description="Timestamp of the most recent session recorded for this agent.",
128
128
  )
129
+ has_agent_service: bool = Field(
130
+ False,
131
+ description="Whether the backend resolved this agent to a typed coding-agent service.",
132
+ )
133
+ agent_service_uid: str | None = Field(
134
+ None,
135
+ description="Public UID of the resolved typed coding-agent service, if one exists.",
136
+ )
137
+ agent_service_automatic_deployment: bool | None = Field(
138
+ None,
139
+ description="Service-level automatic deployment flag for the resolved typed coding-agent service.",
140
+ )
129
141
 
130
142
  @classmethod
131
143
  def get_by_agent_unique_id(cls, agent_unique_id: str, *, timeout=None) -> Agent:
@@ -306,6 +318,77 @@ class Agent(ShareableObjectMixin, BaseObjectOrm, BasePydanticModel):
306
318
  raise_for_response(response, payload=payload)
307
319
  return response.json()
308
320
 
321
+ def get_or_create_session_with_handle(
322
+ self,
323
+ *,
324
+ handle_unique_id: str,
325
+ name: str | None = None,
326
+ llm_provider: str | None = None,
327
+ llm_model: str | None = None,
328
+ llm_thinking: str | None = None,
329
+ session_metadata: dict[str, Any] | None = None,
330
+ timeout=None,
331
+ ) -> AgentSession:
332
+ """
333
+ Get or create this user's reusable session slot for this Agent.
334
+
335
+ Hits:
336
+ POST <detail_url>get_or_create_session_with_handle/
337
+
338
+ Request contract:
339
+ - `handle_unique_id` is required and is the stable session-slot key scoped
340
+ by `(agent, authenticated user)`.
341
+ - `name` is optional display text for a newly created session.
342
+ - LLM fields are creation-time overrides only. If the handle already has a
343
+ current session, the backend returns that session unchanged.
344
+ - `session_metadata` is creation-time caller metadata.
345
+
346
+ The response is expected to contain either a top-level `session` object or a
347
+ direct AgentSession payload. The SDK returns the AgentSession object.
348
+ """
349
+ resolved_handle_unique_id = str(handle_unique_id or "").strip()
350
+ if not resolved_handle_unique_id:
351
+ raise ValueError("handle_unique_id is required")
352
+
353
+ body: dict[str, Any] = {
354
+ "handle_unique_id": resolved_handle_unique_id,
355
+ }
356
+ if name is not None:
357
+ body["name"] = str(name)
358
+ if llm_provider is not None:
359
+ body["llm_provider"] = str(llm_provider)
360
+ if llm_model is not None:
361
+ body["llm_model"] = str(llm_model)
362
+ if llm_thinking is not None:
363
+ body["llm_thinking"] = str(llm_thinking)
364
+ if session_metadata is not None:
365
+ body["session_metadata"] = session_metadata
366
+
367
+ url = f"{self.get_detail_url()}get_or_create_session_with_handle/"
368
+ payload = {"json": serialize_to_json(body)}
369
+ response = make_request(
370
+ s=self.build_session(),
371
+ loaders=self.LOADERS,
372
+ r_type="POST",
373
+ url=url,
374
+ payload=payload,
375
+ time_out=timeout,
376
+ )
377
+ if response.status_code not in (200, 201):
378
+ raise_for_response(response, payload=payload)
379
+
380
+ response_payload = response.json()
381
+ session_payload = (
382
+ response_payload.get("session")
383
+ if isinstance(response_payload, dict)
384
+ else response_payload
385
+ )
386
+ if not isinstance(session_payload, dict):
387
+ raise TypeError(
388
+ "get_or_create_session_with_handle response must include a session object"
389
+ )
390
+ return AgentSession(**session_payload)
391
+
309
392
  def get_latest_session(self, timeout=None):
310
393
  """
311
394
  Fetch the latest recorded session for this agent.
@@ -416,6 +499,10 @@ class AgentSession(BaseObjectOrm, BasePydanticModel):
416
499
  agent_uid: str | None = Field(None, description="Public UID of the agent definition used for this session.")
417
500
  created_by_user_uid: str | None = Field(None, description="Public UID of the actor who created the session.")
418
501
  parent_session_uid: str | None = Field(None, description="Public UID of the parent session, if any.")
502
+ name: str = Field(
503
+ "",
504
+ description="Optional human-readable session name for UI and user-facing history.",
505
+ )
419
506
  created_by_user: int | None = Field(
420
507
  None,
421
508
  exclude=True,
@@ -26,7 +26,7 @@ from mainsequence.client.metatables import (
26
26
  )
27
27
  from mainsequence.instrumentation import tracer
28
28
  from mainsequence.logconf import logger
29
- from mainsequence.meta_tables import PlatformTimeIndexMetaTable
29
+ from mainsequence.meta_tables import PlatformTimeIndexMetaTable, compute_metatable_contract_hash
30
30
 
31
31
  from .. import future_registry
32
32
 
@@ -240,7 +240,7 @@ def _storage_table_identity_summary(storage_table: type[PlatformTimeIndexMetaTab
240
240
  if identifier not in (None, ""):
241
241
  parts.append(f"identifier={identifier!r}")
242
242
  try:
243
- contract_hash = storage_table.get_storage_hash()
243
+ contract_hash = compute_metatable_contract_hash(storage_table)
244
244
  except Exception:
245
245
  contract_hash = None
246
246
  if contract_hash not in (None, ""):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 4.4.3
3
+ Version: 4.4.5
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.3"
7
+ version = "4.4.5"
8
8
  description = "Main Sequence SDK "
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1318,12 +1318,17 @@ def test_agent_runtime_models_deserialize_backend_uid_payloads():
1318
1318
  "llm_thinking": "medium",
1319
1319
  "runtime_config": {"temperature": 0},
1320
1320
  "configuration": {"mode": "analysis"},
1321
- "metadata": {"owner": "quant"},
1322
1321
  "last_session_at": "2026-01-01T00:00:00Z",
1322
+ "has_agent_service": True,
1323
+ "agent_service_uid": service_uid,
1324
+ "agent_service_automatic_deployment": True,
1323
1325
  }
1324
1326
  )
1325
1327
  assert agent.uid == agent_uid
1326
1328
  assert agent.agent_unique_id == "research-copilot"
1329
+ assert agent.has_agent_service is True
1330
+ assert agent.agent_service_uid == service_uid
1331
+ assert agent.agent_service_automatic_deployment is True
1327
1332
 
1328
1333
  search_result = agent_models_mod.AgentSemanticSearchResult.model_validate(
1329
1334
  {
@@ -1347,6 +1352,7 @@ def test_agent_runtime_models_deserialize_backend_uid_payloads():
1347
1352
  "agent_type": "custom",
1348
1353
  "created_by_user_uid": user_uid,
1349
1354
  "parent_session_uid": None,
1355
+ "name": "Research follow-up",
1350
1356
  "status": "running",
1351
1357
  "runtime_state": "running",
1352
1358
  "working": True,
@@ -1371,6 +1377,7 @@ def test_agent_runtime_models_deserialize_backend_uid_payloads():
1371
1377
  )
1372
1378
  assert session.uid == session_uid
1373
1379
  assert session.agent_uid == agent_uid
1380
+ assert session.name == "Research follow-up"
1374
1381
 
1375
1382
  orchestrator = agent_models_mod.UserOrchestratorAgentService.model_validate(
1376
1383
  {
@@ -1548,6 +1555,105 @@ def test_agent_a2a_allocation_sends_caller_session_uid(monkeypatch):
1548
1555
  }
1549
1556
 
1550
1557
 
1558
+ def test_agent_get_or_create_session_with_handle_posts_stable_handle_contract(monkeypatch):
1559
+ captured = {}
1560
+ agent_uid = "e0e75693-4110-464c-93e0-82c7fd9c9a23"
1561
+ session_uid = "3f1cc452-43ec-49cb-b2ba-87dbac164d29"
1562
+ user_uid = "fdf409f7-d16f-4f71-986b-9057db6c7eca"
1563
+ agent = agent_models_mod.Agent(
1564
+ uid=agent_uid,
1565
+ name="Research Copilot",
1566
+ agent_type="custom",
1567
+ agent_unique_id="research-copilot",
1568
+ description="Research assistant.",
1569
+ agent_card=None,
1570
+ llm_provider="openai",
1571
+ llm_model="gpt-5.4",
1572
+ llm_thinking="medium",
1573
+ )
1574
+
1575
+ class FakeResponse:
1576
+ status_code = 201
1577
+ content = b'{"ok": true}'
1578
+
1579
+ @staticmethod
1580
+ def json():
1581
+ return {
1582
+ "created": True,
1583
+ "handle_unique_id": "portfolio-review-q2-2026",
1584
+ "handle_uid": "6f91f6a7-d706-49c6-a910-8667aa0468a5",
1585
+ "session": {
1586
+ "uid": session_uid,
1587
+ "agent_uid": agent_uid,
1588
+ "agent_name": "Research Copilot",
1589
+ "agent_type": "custom",
1590
+ "created_by_user_uid": user_uid,
1591
+ "parent_session_uid": None,
1592
+ "name": "Quarterly portfolio review",
1593
+ "status": "running",
1594
+ "runtime_state": "running",
1595
+ "working": True,
1596
+ "started_at": "2026-01-01T00:00:00Z",
1597
+ "ended_at": None,
1598
+ "llm_provider": "openai",
1599
+ "llm_model": "gpt-5.4",
1600
+ "llm_thinking": "",
1601
+ "engine_name": "codex",
1602
+ "runtime_config_snapshot": {},
1603
+ "error_detail": "",
1604
+ "thread_id": "",
1605
+ "session_metadata": {"portfolio_uid": "portfolio-123"},
1606
+ "bound_handles": [
1607
+ {
1608
+ "handle_unique_id": "portfolio-review-q2-2026",
1609
+ "owner_user_uid": user_uid,
1610
+ "is_locked": False,
1611
+ }
1612
+ ],
1613
+ },
1614
+ }
1615
+
1616
+ def _fake_make_request(*, s, loaders, r_type, url, payload, time_out=None):
1617
+ captured["r_type"] = r_type
1618
+ captured["url"] = url
1619
+ captured["payload"] = payload
1620
+ captured["timeout"] = time_out
1621
+ return FakeResponse()
1622
+
1623
+ monkeypatch.setattr(agent_models_mod, "make_request", _fake_make_request)
1624
+
1625
+ session = agent.get_or_create_session_with_handle(
1626
+ handle_unique_id="portfolio-review-q2-2026",
1627
+ name="Quarterly portfolio review",
1628
+ llm_provider="openai",
1629
+ llm_model="gpt-5.4",
1630
+ llm_thinking="",
1631
+ session_metadata={"portfolio_uid": "portfolio-123"},
1632
+ timeout=13,
1633
+ )
1634
+
1635
+ assert session.uid == session_uid
1636
+ assert session.name == "Quarterly portfolio review"
1637
+ assert captured == {
1638
+ "r_type": "POST",
1639
+ "url": (
1640
+ f"{agent_models_mod.Agent.get_object_url()}/{agent_uid}/"
1641
+ "get_or_create_session_with_handle/"
1642
+ ),
1643
+ "payload": {
1644
+ "json": {
1645
+ "handle_unique_id": "portfolio-review-q2-2026",
1646
+ "name": "Quarterly portfolio review",
1647
+ "llm_provider": "openai",
1648
+ "llm_model": "gpt-5.4",
1649
+ "llm_thinking": "",
1650
+ "session_metadata": {"portfolio_uid": "portfolio-123"},
1651
+ }
1652
+ },
1653
+ "timeout": 13,
1654
+ }
1655
+
1656
+
1551
1657
  def _class_base_names_from_source(path: pathlib.Path) -> dict[str, list[str]]:
1552
1658
  tree = ast.parse(path.read_text(encoding="utf-8"))
1553
1659
  out: dict[str, list[str]] = {}
File without changes
File without changes
File without changes