agentex-sdk 0.8.0__py3-none-any.whl → 0.8.2__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.
agentex/_client.py CHANGED
@@ -31,7 +31,7 @@ from ._base_client import (
31
31
  )
32
32
 
33
33
  if TYPE_CHECKING:
34
- from .resources import spans, tasks, agents, events, states, tracker, messages
34
+ from .resources import spans, tasks, agents, events, states, tracker, messages, deployment_history
35
35
  from .resources.spans import SpansResource, AsyncSpansResource
36
36
  from .resources.tasks import TasksResource, AsyncTasksResource
37
37
  from .resources.agents import AgentsResource, AsyncAgentsResource
@@ -39,6 +39,7 @@ if TYPE_CHECKING:
39
39
  from .resources.states import StatesResource, AsyncStatesResource
40
40
  from .resources.tracker import TrackerResource, AsyncTrackerResource
41
41
  from .resources.messages.messages import MessagesResource, AsyncMessagesResource
42
+ from .resources.deployment_history import DeploymentHistoryResource, AsyncDeploymentHistoryResource
42
43
 
43
44
  __all__ = [
44
45
  "ENVIRONMENTS",
@@ -175,6 +176,12 @@ class Agentex(SyncAPIClient):
175
176
 
176
177
  return TrackerResource(self)
177
178
 
179
+ @cached_property
180
+ def deployment_history(self) -> DeploymentHistoryResource:
181
+ from .resources.deployment_history import DeploymentHistoryResource
182
+
183
+ return DeploymentHistoryResource(self)
184
+
178
185
  @cached_property
179
186
  def with_raw_response(self) -> AgentexWithRawResponse:
180
187
  return AgentexWithRawResponse(self)
@@ -409,6 +416,12 @@ class AsyncAgentex(AsyncAPIClient):
409
416
 
410
417
  return AsyncTrackerResource(self)
411
418
 
419
+ @cached_property
420
+ def deployment_history(self) -> AsyncDeploymentHistoryResource:
421
+ from .resources.deployment_history import AsyncDeploymentHistoryResource
422
+
423
+ return AsyncDeploymentHistoryResource(self)
424
+
412
425
  @cached_property
413
426
  def with_raw_response(self) -> AsyncAgentexWithRawResponse:
414
427
  return AsyncAgentexWithRawResponse(self)
@@ -574,6 +587,12 @@ class AgentexWithRawResponse:
574
587
 
575
588
  return TrackerResourceWithRawResponse(self._client.tracker)
576
589
 
590
+ @cached_property
591
+ def deployment_history(self) -> deployment_history.DeploymentHistoryResourceWithRawResponse:
592
+ from .resources.deployment_history import DeploymentHistoryResourceWithRawResponse
593
+
594
+ return DeploymentHistoryResourceWithRawResponse(self._client.deployment_history)
595
+
577
596
 
578
597
  class AsyncAgentexWithRawResponse:
579
598
  _client: AsyncAgentex
@@ -623,6 +642,12 @@ class AsyncAgentexWithRawResponse:
623
642
 
624
643
  return AsyncTrackerResourceWithRawResponse(self._client.tracker)
625
644
 
645
+ @cached_property
646
+ def deployment_history(self) -> deployment_history.AsyncDeploymentHistoryResourceWithRawResponse:
647
+ from .resources.deployment_history import AsyncDeploymentHistoryResourceWithRawResponse
648
+
649
+ return AsyncDeploymentHistoryResourceWithRawResponse(self._client.deployment_history)
650
+
626
651
 
627
652
  class AgentexWithStreamedResponse:
628
653
  _client: Agentex
@@ -672,6 +697,12 @@ class AgentexWithStreamedResponse:
672
697
 
673
698
  return TrackerResourceWithStreamingResponse(self._client.tracker)
674
699
 
700
+ @cached_property
701
+ def deployment_history(self) -> deployment_history.DeploymentHistoryResourceWithStreamingResponse:
702
+ from .resources.deployment_history import DeploymentHistoryResourceWithStreamingResponse
703
+
704
+ return DeploymentHistoryResourceWithStreamingResponse(self._client.deployment_history)
705
+
675
706
 
676
707
  class AsyncAgentexWithStreamedResponse:
677
708
  _client: AsyncAgentex
@@ -721,6 +752,12 @@ class AsyncAgentexWithStreamedResponse:
721
752
 
722
753
  return AsyncTrackerResourceWithStreamingResponse(self._client.tracker)
723
754
 
755
+ @cached_property
756
+ def deployment_history(self) -> deployment_history.AsyncDeploymentHistoryResourceWithStreamingResponse:
757
+ from .resources.deployment_history import AsyncDeploymentHistoryResourceWithStreamingResponse
758
+
759
+ return AsyncDeploymentHistoryResourceWithStreamingResponse(self._client.deployment_history)
760
+
724
761
 
725
762
  Client = Agentex
726
763
 
agentex/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "agentex"
4
- __version__ = "0.8.0" # x-release-please-version
4
+ __version__ = "0.8.2" # x-release-please-version
@@ -83,26 +83,24 @@ def delete(
83
83
  @agents.command()
84
84
  def cleanup_workflows(
85
85
  agent_name: str = typer.Argument(..., help="Name of the agent to cleanup workflows for"),
86
- force: bool = typer.Option(False, help="Force cleanup using direct Temporal termination (bypasses development check)"),
86
+ force: bool = typer.Option(
87
+ False, help="Force cleanup using direct Temporal termination (bypasses development check)"
88
+ ),
87
89
  ):
88
90
  """
89
91
  Clean up all running workflows for an agent.
90
-
92
+
91
93
  By default, uses graceful cancellation via agent RPC.
92
94
  With --force, directly terminates workflows via Temporal client.
93
95
  This is a convenience command that does the same thing as 'agentex tasks cleanup'.
94
96
  """
95
97
  try:
96
98
  console.print(f"[blue]Cleaning up workflows for agent '{agent_name}'...[/blue]")
97
-
98
- cleanup_agent_workflows(
99
- agent_name=agent_name,
100
- force=force,
101
- development_only=True
102
- )
103
-
99
+
100
+ cleanup_agent_workflows(agent_name=agent_name, force=force, development_only=True)
101
+
104
102
  console.print(f"[green]✓ Workflow cleanup completed for agent '{agent_name}'[/green]")
105
-
103
+
106
104
  except Exception as e:
107
105
  console.print(f"[red]Cleanup failed: {str(e)}[/red]")
108
106
  logger.exception("Agent workflow cleanup failed")
@@ -112,12 +110,8 @@ def cleanup_workflows(
112
110
  @agents.command()
113
111
  def build(
114
112
  manifest: str = typer.Option(..., help="Path to the manifest you want to use"),
115
- registry: str | None = typer.Option(
116
- None, help="Registry URL for pushing the built image"
117
- ),
118
- repository_name: str | None = typer.Option(
119
- None, help="Repository name to use for the built image"
120
- ),
113
+ registry: str | None = typer.Option(None, help="Registry URL for pushing the built image"),
114
+ repository_name: str | None = typer.Option(None, help="Repository name to use for the built image"),
121
115
  platforms: str | None = typer.Option(
122
116
  None, help="Platform to build the image for. Please enter a comma separated list of platforms."
123
117
  ),
@@ -126,9 +120,7 @@ def build(
126
120
  None,
127
121
  help="Docker build secret in the format 'id=secret-id,src=path-to-secret-file'",
128
122
  ),
129
- tag: str | None = typer.Option(
130
- None, help="Image tag to use (defaults to 'latest')"
131
- ),
123
+ tag: str | None = typer.Option(None, help="Image tag to use (defaults to 'latest')"),
132
124
  build_arg: builtins.list[str] | None = typer.Option( # noqa: B008
133
125
  None,
134
126
  help="Docker build argument in the format 'KEY=VALUE' (can be used multiple times)",
@@ -143,7 +135,7 @@ def build(
143
135
  if push and not registry:
144
136
  typer.echo("Error: --registry is required when --push is enabled", err=True)
145
137
  raise typer.Exit(1)
146
-
138
+
147
139
  # Only proceed with build if we have a registry (for now, to match existing behavior)
148
140
  if not registry:
149
141
  typer.echo("No registry provided, skipping image build")
@@ -175,10 +167,7 @@ def build(
175
167
  @agents.command()
176
168
  def run(
177
169
  manifest: str = typer.Option(..., help="Path to the manifest you want to use"),
178
- cleanup_on_start: bool = typer.Option(
179
- False,
180
- help="Clean up existing workflows for this agent before starting"
181
- ),
170
+ cleanup_on_start: bool = typer.Option(False, help="Clean up existing workflows for this agent before starting"),
182
171
  # Debug options
183
172
  debug: bool = typer.Option(False, help="Enable debug mode for both worker and ACP (disables auto-reload)"),
184
173
  debug_worker: bool = typer.Option(False, help="Enable debug mode for temporal worker only"),
@@ -190,26 +179,22 @@ def run(
190
179
  Run an agent locally from the given manifest.
191
180
  """
192
181
  typer.echo(f"Running agent from manifest: {manifest}")
193
-
182
+
194
183
  # Optionally cleanup existing workflows before starting
195
184
  if cleanup_on_start:
196
185
  try:
197
186
  # Parse manifest to get agent name
198
187
  manifest_obj = AgentManifest.from_yaml(file_path=manifest)
199
188
  agent_name = manifest_obj.agent.name
200
-
189
+
201
190
  console.print(f"[yellow]Cleaning up existing workflows for agent '{agent_name}'...[/yellow]")
202
- cleanup_agent_workflows(
203
- agent_name=agent_name,
204
- force=False,
205
- development_only=True
206
- )
191
+ cleanup_agent_workflows(agent_name=agent_name, force=False, development_only=True)
207
192
  console.print("[green]✓ Pre-run cleanup completed[/green]")
208
-
193
+
209
194
  except Exception as e:
210
195
  console.print(f"[yellow]⚠ Pre-run cleanup failed: {str(e)}[/yellow]")
211
196
  logger.warning(f"Pre-run cleanup failed: {e}")
212
-
197
+
213
198
  # Create debug configuration based on CLI flags
214
199
  debug_config = None
215
200
  if debug or debug_worker or debug_acp:
@@ -224,19 +209,19 @@ def run(
224
209
  mode = DebugMode.ACP
225
210
  else:
226
211
  mode = DebugMode.NONE
227
-
212
+
228
213
  debug_config = DebugConfig(
229
214
  enabled=True,
230
215
  mode=mode,
231
216
  port=debug_port,
232
217
  wait_for_attach=wait_for_debugger,
233
- auto_port=False # Use fixed port to match VS Code launch.json
218
+ auto_port=False, # Use fixed port to match VS Code launch.json
234
219
  )
235
-
220
+
236
221
  console.print(f"[blue]🐛 Debug mode enabled: {mode.value}[/blue]")
237
222
  if wait_for_debugger:
238
223
  console.print("[yellow]⏳ Processes will wait for debugger attachment[/yellow]")
239
-
224
+
240
225
  try:
241
226
  run_agent(manifest_path=manifest, debug_config=debug_config)
242
227
  except Exception as e:
@@ -247,30 +232,23 @@ def run(
247
232
 
248
233
  @agents.command()
249
234
  def deploy(
250
- cluster: str = typer.Option(
251
- ..., help="Target cluster name (must match kubectl context)"
252
- ),
235
+ cluster: str = typer.Option(..., help="Target cluster name (must match kubectl context)"),
253
236
  manifest: str = typer.Option("manifest.yaml", help="Path to the manifest file"),
254
237
  namespace: str | None = typer.Option(
255
238
  None,
256
239
  help="Override Kubernetes namespace (defaults to namespace from environments.yaml)",
257
240
  ),
258
241
  environment: str | None = typer.Option(
259
- None, help="Environment name (dev, prod, etc.) - must be defined in environments.yaml. If not provided, the namespace must be set explicitly."
260
- ),
261
- tag: str | None = typer.Option(None, help="Override the image tag for deployment"),
262
- repository: str | None = typer.Option(
263
- None, help="Override the repository for deployment"
264
- ),
265
- interactive: bool = typer.Option(
266
- True, "--interactive/--no-interactive", help="Enable interactive prompts"
242
+ None,
243
+ help="Environment name (dev, prod, etc.) - must be defined in environments.yaml. If not provided, the namespace must be set explicitly.",
267
244
  ),
245
+ tag: str | None = typer.Option(None, help="Override the image tag for deployment"),
246
+ repository: str | None = typer.Option(None, help="Override the repository for deployment"),
247
+ interactive: bool = typer.Option(True, "--interactive/--no-interactive", help="Enable interactive prompts"),
268
248
  ):
269
249
  """Deploy an agent to a Kubernetes cluster using Helm"""
270
250
 
271
- console.print(
272
- Panel.fit("🚀 [bold blue]Deploy Agent[/bold blue]", border_style="blue")
273
- )
251
+ console.print(Panel.fit("🚀 [bold blue]Deploy Agent[/bold blue]", border_style="blue"))
274
252
 
275
253
  try:
276
254
  # Validate manifest exists
@@ -281,17 +259,12 @@ def deploy(
281
259
 
282
260
  # Validate manifest and environments configuration
283
261
  try:
284
- if environment:
285
- _, environments_config = validate_manifest_and_environments(
286
- str(manifest_path),
287
- required_environment=environment
288
- )
289
- agent_env_config = environments_config.get_config_for_env(environment)
290
- console.print(f"[green]✓[/green] Environment config validated: {environment}")
291
- else:
292
- agent_env_config = None
293
- console.print(f"[yellow]⚠[/yellow] No environment provided, skipping environment-specific config")
294
-
262
+ _, environments_config = validate_manifest_and_environments(
263
+ str(manifest_path), required_environment=environment
264
+ )
265
+ agent_env_config = environments_config.get_config_for_env(environment)
266
+ console.print(f"[green]✓[/green] Environment config validated: {environment}")
267
+
295
268
  except EnvironmentsValidationError as e:
296
269
  error_msg = generate_helpful_error_message(e, "Environment validation failed")
297
270
  console.print(f"[red]Configuration Error:[/red]\n{error_msg}")
@@ -310,9 +283,13 @@ def deploy(
310
283
  console.print(f"[blue]ℹ[/blue] Using namespace from environments.yaml: {namespace_from_config}")
311
284
  namespace = namespace_from_config
312
285
  else:
313
- raise DeploymentError(f"No namespace found in environments.yaml for environment: {environment}, and not passed in as --namespace")
286
+ raise DeploymentError(
287
+ f"No namespace found in environments.yaml for environment: {environment}, and not passed in as --namespace"
288
+ )
314
289
  elif not namespace:
315
- raise DeploymentError("No namespace provided, and not passed in as --namespace and no environment provided to read from an environments.yaml file")
290
+ raise DeploymentError(
291
+ "No namespace provided, and not passed in as --namespace and no environment provided to read from an environments.yaml file"
292
+ )
316
293
 
317
294
  # Confirm deployment (only in interactive mode)
318
295
  console.print("\n[bold]Deployment Summary:[/bold]")
@@ -325,9 +302,7 @@ def deploy(
325
302
 
326
303
  if interactive:
327
304
  proceed = questionary.confirm("Proceed with deployment?").ask()
328
- proceed = handle_questionary_cancellation(
329
- proceed, "deployment confirmation"
330
- )
305
+ proceed = handle_questionary_cancellation(proceed, "deployment confirmation")
331
306
 
332
307
  if not proceed:
333
308
  console.print("Deployment cancelled")
@@ -337,9 +312,7 @@ def deploy(
337
312
 
338
313
  check_and_switch_cluster_context(cluster)
339
314
  if not validate_namespace(namespace, cluster):
340
- console.print(
341
- f"[red]Error:[/red] Namespace '{namespace}' does not exist in cluster '{cluster}'"
342
- )
315
+ console.print(f"[red]Error:[/red] Namespace '{namespace}' does not exist in cluster '{cluster}'")
343
316
  raise typer.Exit(1)
344
317
 
345
318
  deploy_overrides = InputDeployOverrides(repository=repository, image_tag=tag)
@@ -356,9 +329,7 @@ def deploy(
356
329
  # Use the already loaded manifest object
357
330
  release_name = f"{manifest_obj.agent.name}-{cluster}"
358
331
 
359
- console.print(
360
- "\n[bold green]🎉 Deployment completed successfully![/bold green]"
361
- )
332
+ console.print("\n[bold green]🎉 Deployment completed successfully![/bold green]")
362
333
  console.print("\nTo check deployment status:")
363
334
  console.print(f" kubectl get pods -n {namespace}")
364
335
  console.print(f" helm status {release_name} -n {namespace}")
@@ -18,13 +18,12 @@ from agentex.lib.utils.model_utils import BaseModel as UtilsBaseModel
18
18
 
19
19
  class AgentAuthConfig(BaseModel):
20
20
  """Authentication configuration for an agent in a specific environment."""
21
-
21
+
22
22
  principal: Dict[str, Any] = Field(
23
- ...,
24
- description="Principal configuration for agent authorization and registration"
23
+ ..., description="Principal configuration for agent authorization and registration"
25
24
  )
26
-
27
- @field_validator('principal')
25
+
26
+ @field_validator("principal")
28
27
  @classmethod
29
28
  def validate_principal_required_fields(cls, v: Any) -> Dict[str, Any]:
30
29
  """Ensure principal has required fields for agent registration."""
@@ -35,125 +34,166 @@ class AgentAuthConfig(BaseModel):
35
34
 
36
35
  class AgentKubernetesConfig(BaseModel):
37
36
  """Kubernetes configuration for an agent in a specific environment."""
38
-
39
- namespace: str = Field(
40
- ...,
41
- description="Kubernetes namespace where the agent will be deployed"
42
- )
43
-
44
- @field_validator('namespace')
37
+
38
+ namespace: str = Field(..., description="Kubernetes namespace where the agent will be deployed")
39
+
40
+ @field_validator("namespace")
45
41
  @classmethod
46
42
  def validate_namespace_format(cls, v: str) -> str:
47
43
  """Ensure namespace follows Kubernetes naming conventions."""
48
44
  if not v or not v.strip():
49
45
  raise ValueError("Namespace cannot be empty")
50
-
46
+
51
47
  # Basic Kubernetes namespace validation
52
48
  namespace = v.strip().lower()
53
- if not namespace.replace('-', '').replace('.', '').isalnum():
54
- raise ValueError(
55
- f"Namespace '{v}' must contain only lowercase letters, numbers, "
56
- "hyphens, and periods"
57
- )
58
-
49
+ if not namespace.replace("-", "").replace(".", "").isalnum():
50
+ raise ValueError(f"Namespace '{v}' must contain only lowercase letters, numbers, hyphens, and periods")
51
+
59
52
  if len(namespace) > 63:
60
53
  raise ValueError(f"Namespace '{v}' cannot exceed 63 characters")
61
-
54
+
62
55
  return namespace
63
56
 
64
57
 
65
58
  class AgentEnvironmentConfig(BaseModel):
66
59
  """Complete configuration for an agent in a specific environment."""
67
-
68
- kubernetes: AgentKubernetesConfig | None = Field(
69
- default=None,
70
- description="Kubernetes deployment configuration"
71
- )
72
- auth: AgentAuthConfig = Field(
73
- ...,
74
- description="Authentication and authorization configuration"
75
- )
76
- helm_repository_name: str = Field(
77
- default="scale-egp",
78
- description="Helm repository name for the environment"
60
+
61
+ kubernetes: AgentKubernetesConfig | None = Field(default=None, description="Kubernetes deployment configuration")
62
+ environment: str | None = Field(
63
+ default=None,
64
+ description="The environment keyword that this specific environment maps to: either dev, staging, prod",
79
65
  )
66
+ auth: AgentAuthConfig = Field(..., description="Authentication and authorization configuration")
67
+ helm_repository_name: str = Field(default="scale-egp", description="Helm repository name for the environment")
80
68
  helm_repository_url: str = Field(
81
- default="https://scale-egp-helm-charts-us-west-2.s3.amazonaws.com/charts",
82
- description="Helm repository url for the environment"
69
+ default="https://scale-egp-helm-charts-us-west-2.s3.amazonaws.com/charts",
70
+ description="Helm repository url for the environment",
83
71
  )
84
72
  helm_overrides: Dict[str, Any] = Field(
85
- default_factory=dict,
86
- description="Helm chart value overrides for environment-specific tuning"
73
+ default_factory=dict, description="Helm chart value overrides for environment-specific tuning"
87
74
  )
88
75
 
89
76
 
90
77
  class AgentEnvironmentsConfig(UtilsBaseModel):
91
78
  """All environment configurations for an agent."""
92
-
93
- schema_version: str = Field(
94
- default="v1",
95
- description="Schema version for validation and compatibility"
96
- )
79
+
80
+ schema_version: str = Field(default="v1", description="Schema version for validation and compatibility")
97
81
  environments: Dict[str, AgentEnvironmentConfig] = Field(
98
- ...,
99
- description="Environment-specific configurations (dev, prod, etc.)"
82
+ ..., description="Environment-specific configurations (dev, prod, etc.)"
100
83
  )
101
-
102
- @field_validator('schema_version')
84
+
85
+ @field_validator("schema_version")
103
86
  @classmethod
104
87
  def validate_schema_version(cls, v: str) -> str:
105
88
  """Ensure schema version is supported."""
106
- supported_versions = ['v1']
89
+ supported_versions = ["v1"]
107
90
  if v not in supported_versions:
108
- raise ValueError(
109
- f"Schema version '{v}' not supported. "
110
- f"Supported versions: {', '.join(supported_versions)}"
111
- )
91
+ raise ValueError(f"Schema version '{v}' not supported. Supported versions: {', '.join(supported_versions)}")
112
92
  return v
113
-
114
- @field_validator('environments')
93
+
94
+ @field_validator("environments")
115
95
  @classmethod
116
96
  def validate_environments_not_empty(cls, v: Dict[str, AgentEnvironmentConfig]) -> Dict[str, AgentEnvironmentConfig]:
117
97
  """Ensure at least one environment is defined."""
118
98
  if not v:
119
99
  raise ValueError("At least one environment must be defined")
120
100
  return v
121
-
101
+
122
102
  def get_config_for_env(self, env_name: str) -> AgentEnvironmentConfig:
123
103
  """Get configuration for a specific environment.
124
-
104
+
125
105
  Args:
126
106
  env_name: Name of the environment (e.g., 'dev', 'prod')
127
-
107
+
128
108
  Returns:
129
109
  AgentEnvironmentConfig for the specified environment
130
-
110
+
131
111
  Raises:
132
112
  ValueError: If environment is not found
133
113
  """
134
114
  if env_name not in self.environments:
135
- available_envs = ', '.join(self.environments.keys())
115
+ available_envs = ", ".join(self.environments.keys())
136
116
  raise ValueError(
137
- f"Environment '{env_name}' not found in environments.yaml. "
138
- f"Available environments: {available_envs}"
117
+ f"Environment '{env_name}' not found in environments.yaml. Available environments: {available_envs}"
139
118
  )
140
119
  return self.environments[env_name]
141
-
120
+
121
+ def get_configs_for_env(self, env_target: str) -> dict[str, AgentEnvironmentConfig]:
122
+ """Get configuration for a specific environment based on the expected mapping.
123
+ The environment is either:
124
+ 1. explicitly specified like so using a key-map in the environments conifg:
125
+ environments:
126
+ dev-aws:
127
+ environment: "dev"
128
+ kubernetes:
129
+ namespace: "sgp-000-hello-acp"
130
+ auth:
131
+ principal:
132
+ user_id: 73d0c8bd-4726-434c-9686-eb627d89f078
133
+ account_id: 6887f093600ecd59bbbd3095
134
+ helm_overrides:
135
+
136
+ or: it it can be defined at the top level:
137
+ dev:
138
+ kubernetes:
139
+ namespace: "sgp-000-hello-acp"
140
+ auth:
141
+ principal:
142
+ user_id: 73d0c8bd-4726-434c-9686-eb627d89f078
143
+ account_id: 6887f093600ecd59bbbd3095
144
+ helm_overrides:
145
+
146
+ if the environment field is not explicitly set, we assume its the same as
147
+ the name of the environment
148
+ Args:
149
+ env_target: Name of the environment target (e.g., 'dev', 'prod')
150
+
151
+ Returns:
152
+ AgentEnvironmentConfig for the specified environment
153
+
154
+ Raises:
155
+ ValueError: If environment is not found
156
+ """
157
+ envs_to_deploy = {}
158
+ if env_target in self.environments:
159
+ # this supports if the top-level key is just "dev, staging, etc" and matches
160
+ # the environment name exactly without any explicit mapping
161
+ envs_to_deploy[env_target] = self.environments[env_target]
162
+
163
+ for env_name, config in self.environments.items():
164
+ if config.environment == env_target:
165
+ envs_to_deploy[env_name] = config
166
+
167
+ if len(envs_to_deploy) == 0:
168
+ ## this just finds environments for each target, so "available_envs" refers to each target environment
169
+
170
+ available_envs = set()
171
+ for env_name, config in self.environments.items():
172
+ if config.environment is not None:
173
+ available_envs.add(config.environment)
174
+ else:
175
+ available_envs.add(env_name)
176
+ raise ValueError(
177
+ f"Environment '{env_target}' not found in environments.yaml. Available environments: {available_envs}"
178
+ )
179
+
180
+ return envs_to_deploy
181
+
142
182
  def list_environments(self) -> list[str]:
143
183
  """Get list of all configured environment names."""
144
184
  return list(self.environments.keys())
145
-
185
+
146
186
  @classmethod
147
187
  @override
148
188
  def from_yaml(cls, file_path: str) -> "AgentEnvironmentsConfig":
149
189
  """Load configuration from environments.yaml file.
150
-
190
+
151
191
  Args:
152
192
  file_path: Path to environments.yaml file
153
-
193
+
154
194
  Returns:
155
195
  Parsed and validated AgentEnvironmentsConfig
156
-
196
+
157
197
  Raises:
158
198
  FileNotFoundError: If file doesn't exist
159
199
  ValueError: If file is invalid or doesn't validate
@@ -161,16 +201,16 @@ class AgentEnvironmentsConfig(UtilsBaseModel):
161
201
  path = Path(file_path)
162
202
  if not path.exists():
163
203
  raise FileNotFoundError(f"environments.yaml not found: {file_path}")
164
-
204
+
165
205
  try:
166
- with open(path, 'r') as f:
206
+ with open(path, "r") as f:
167
207
  data = yaml.safe_load(f)
168
-
208
+
169
209
  if not data:
170
210
  raise ValueError("environments.yaml file is empty")
171
-
211
+
172
212
  return cls.model_validate(data)
173
-
213
+
174
214
  except yaml.YAMLError as e:
175
215
  raise ValueError(f"Invalid YAML format in {file_path}: {e}") from e
176
216
  except Exception as e:
@@ -179,18 +219,18 @@ class AgentEnvironmentsConfig(UtilsBaseModel):
179
219
 
180
220
  def load_environments_config_from_manifest_dir(manifest_dir: Path) -> AgentEnvironmentsConfig | None:
181
221
  """Helper function to load environments.yaml from same directory as manifest.yaml.
182
-
222
+
183
223
  Args:
184
224
  manifest_dir: Directory containing manifest.yaml
185
-
225
+
186
226
  Returns:
187
227
  AgentEnvironmentsConfig if environments.yaml exists, None otherwise
188
-
228
+
189
229
  Raises:
190
230
  ValueError: If environments.yaml exists but is invalid
191
231
  """
192
232
  environments_file = manifest_dir / "environments.yaml"
193
233
  if not environments_file.exists():
194
234
  return None
195
-
235
+
196
236
  return AgentEnvironmentsConfig.from_yaml(str(environments_file))
@@ -4,6 +4,7 @@ Validation framework for agent configuration files.
4
4
  This module provides validation functions for agent configurations,
5
5
  with clear error messages and best practices enforcement.
6
6
  """
7
+
7
8
  from __future__ import annotations
8
9
 
9
10
  from typing import Any, Dict, List, Optional
@@ -17,7 +18,7 @@ logger = make_logger(__name__)
17
18
 
18
19
  class ConfigValidationError(Exception):
19
20
  """Exception raised when configuration validation fails."""
20
-
21
+
21
22
  def __init__(self, message: str, file_path: Optional[str] = None):
22
23
  self.file_path = file_path
23
24
  super().__init__(message)
@@ -25,88 +26,95 @@ class ConfigValidationError(Exception):
25
26
 
26
27
  class EnvironmentsValidationError(ConfigValidationError):
27
28
  """Exception raised when environments.yaml validation fails."""
29
+
28
30
  pass
29
31
 
30
32
 
31
33
  def validate_environments_config(
32
- environments_config: AgentEnvironmentsConfig,
33
- required_environments: Optional[List[str]] = None
34
+ environments_config: AgentEnvironmentsConfig, required_environments: Optional[List[str]] = None
34
35
  ) -> None:
35
36
  """
36
37
  Validate environments configuration with comprehensive checks.
37
-
38
+
38
39
  Args:
39
40
  environments_config: The loaded environments configuration
40
41
  required_environments: List of environment names that must be present
41
-
42
+
42
43
  Raises:
43
44
  EnvironmentsValidationError: If validation fails
44
45
  """
45
46
  # Check for required environments
46
47
  if required_environments:
48
+ # this must exist as a top-level key or via the environment indicator
47
49
  missing_envs: List[str] = []
50
+ environment_mappings = [env.environment for env in environments_config.environments.values() if env.environment]
51
+ top_level_envs = [env for env in environments_config.environments]
52
+ all_envs = set(environment_mappings + top_level_envs)
48
53
  for env_name in required_environments:
49
- if env_name not in environments_config.environments:
54
+ if env_name not in all_envs:
50
55
  missing_envs.append(env_name)
51
-
56
+
52
57
  if missing_envs:
53
- available_envs = list(environments_config.environments.keys())
54
58
  raise EnvironmentsValidationError(
55
59
  f"Missing required environments: {', '.join(missing_envs)}. "
56
- f"Available environments: {', '.join(available_envs)}"
60
+ f"Available environments: {', '.join(all_envs)}"
57
61
  )
58
-
62
+
63
+ # if environment mappings are set, you cannot have a top-level env_name that maps to an `environment: value`
64
+ # and another environment that has the mapping i.e.
65
+ # enviorments:
66
+ # dev:
67
+ # ....
68
+ # dev1:
69
+ # environment: dev
70
+ # this is invalid because its unclear if "dev" refers to just that top-level environment or the mapping
71
+ #
59
72
  # Validate each environment configuration
60
73
  for env_name, env_config in environments_config.environments.items():
61
74
  try:
62
75
  _validate_single_environment_config(env_name, env_config)
63
76
  except Exception as e:
64
- raise EnvironmentsValidationError(
65
- f"Environment '{env_name}' configuration error: {str(e)}"
66
- ) from e
77
+ raise EnvironmentsValidationError(f"Environment '{env_name}' configuration error: {str(e)}") from e
67
78
 
68
79
 
69
80
  def _validate_single_environment_config(env_name: str, env_config: AgentEnvironmentConfig) -> None:
70
81
  """
71
82
  Validate a single environment configuration.
72
-
83
+
73
84
  Args:
74
85
  env_name: Name of the environment
75
86
  env_config: AgentEnvironmentConfig instance
76
-
87
+
77
88
  Raises:
78
89
  ValueError: If validation fails
79
90
  """
80
91
  # Validate namespace naming conventions if kubernetes config exists
81
92
  if env_config.kubernetes and env_config.kubernetes.namespace:
82
93
  namespace = env_config.kubernetes.namespace
83
-
94
+
84
95
  # Check for common namespace naming issues
85
96
  if namespace != namespace.lower():
86
97
  logger.warning(
87
- f"Namespace '{namespace}' contains uppercase letters. "
88
- "Kubernetes namespaces should be lowercase."
98
+ f"Namespace '{namespace}' contains uppercase letters. Kubernetes namespaces should be lowercase."
89
99
  )
90
-
91
- if namespace.startswith('-') or namespace.endswith('-'):
92
- raise ValueError(
93
- f"Namespace '{namespace}' cannot start or end with hyphens"
94
- )
95
-
100
+
101
+ if namespace.startswith("-") or namespace.endswith("-"):
102
+ raise ValueError(f"Namespace '{namespace}' cannot start or end with hyphens")
103
+
96
104
  # Validate auth principal
97
105
  principal = env_config.auth.principal
98
- if not principal.get('user_id'):
106
+ if not principal.get("user_id"):
99
107
  raise ValueError("Auth principal must contain non-empty 'user_id'")
100
-
108
+
101
109
  # Check for environment-specific user_id patterns
102
- user_id = principal['user_id']
110
+ user_id = principal["user_id"]
103
111
  if isinstance(user_id, str):
104
- if not any(env_name.lower() in user_id.lower() for env_name in ['dev', 'prod', 'staging', env_name]):
112
+ if not any(env_name.lower() in user_id.lower() for env_name in ["dev", "prod", "staging", env_name]):
105
113
  logger.warning(
106
114
  f"User ID '{user_id}' doesn't contain environment indicator. "
107
115
  f"Consider including '{env_name}' in the user_id for clarity."
108
116
  )
109
-
117
+
110
118
  # Validate helm overrides if present
111
119
  if env_config.helm_overrides:
112
120
  _validate_helm_overrides(env_config.helm_overrides)
@@ -115,26 +123,26 @@ def _validate_single_environment_config(env_name: str, env_config: AgentEnvironm
115
123
  def _validate_helm_overrides(helm_overrides: Dict[str, Any]) -> None:
116
124
  """
117
125
  Validate helm override configuration.
118
-
126
+
119
127
  Args:
120
128
  helm_overrides: Dictionary of helm overrides
121
-
129
+
122
130
  Raises:
123
131
  ValueError: If validation fails
124
132
  """
125
133
  # Check for common helm override issues
126
- if 'resources' in helm_overrides:
127
- resources = helm_overrides['resources']
134
+ if "resources" in helm_overrides:
135
+ resources = helm_overrides["resources"]
128
136
  if isinstance(resources, dict):
129
137
  # Validate resource format
130
- if 'requests' in resources or 'limits' in resources:
131
- for resource_type in ['requests', 'limits']:
138
+ if "requests" in resources or "limits" in resources:
139
+ for resource_type in ["requests", "limits"]:
132
140
  if resource_type in resources:
133
141
  resource_config: Any = resources[resource_type]
134
142
  if isinstance(resource_config, dict):
135
143
  # Check for valid resource specifications
136
144
  for key, value in resource_config.items():
137
- if key in ['cpu', 'memory'] and not isinstance(value, str):
145
+ if key in ["cpu", "memory"] and not isinstance(value, str):
138
146
  logger.warning(
139
147
  f"Resource {key} should be a string (e.g., '500m', '1Gi'), "
140
148
  f"got {type(value).__name__}: {value}"
@@ -144,13 +152,13 @@ def _validate_helm_overrides(helm_overrides: Dict[str, Any]) -> None:
144
152
  def validate_environments_yaml_file(file_path: str) -> AgentEnvironmentsConfig:
145
153
  """
146
154
  Load and validate environments.yaml file.
147
-
155
+
148
156
  Args:
149
157
  file_path: Path to environments.yaml file
150
-
158
+
151
159
  Returns:
152
160
  Validated AgentEnvironmentsConfig
153
-
161
+
154
162
  Raises:
155
163
  EnvironmentsValidationError: If file is invalid
156
164
  """
@@ -164,66 +172,59 @@ def validate_environments_yaml_file(file_path: str) -> AgentEnvironmentsConfig:
164
172
  "📋 Why required:\n"
165
173
  " Environment-specific settings (auth, namespace, resources)\n"
166
174
  " must be separated from global manifest for proper isolation.",
167
- file_path=file_path
175
+ file_path=file_path,
168
176
  ) from None
169
177
  except Exception as e:
170
- raise EnvironmentsValidationError(
171
- f"Invalid environments.yaml file: {str(e)}",
172
- file_path=file_path
173
- ) from e
178
+ raise EnvironmentsValidationError(f"Invalid environments.yaml file: {str(e)}", file_path=file_path) from e
174
179
 
175
180
 
176
181
  def validate_manifest_and_environments(
177
- manifest_path: str,
178
- required_environment: Optional[str] = None
182
+ manifest_path: str, required_environment: Optional[str] = None
179
183
  ) -> tuple[str, AgentEnvironmentsConfig]:
180
184
  """
181
185
  Validate both manifest.yaml and environments.yaml files together.
182
-
186
+
183
187
  Args:
184
188
  manifest_path: Path to manifest.yaml file
185
189
  required_environment: Specific environment that must be present
186
-
190
+
187
191
  Returns:
188
192
  Tuple of (manifest_path, environments_config)
189
-
193
+
190
194
  Raises:
191
195
  ConfigValidationError: If validation fails
192
196
  """
193
197
  manifest_file = Path(manifest_path)
194
198
  if not manifest_file.exists():
195
199
  raise ConfigValidationError(f"Manifest file not found: {manifest_path}")
196
-
200
+
197
201
  # Look for environments.yaml in same directory
198
202
  environments_file = manifest_file.parent / "environments.yaml"
199
203
  environments_config = validate_environments_yaml_file(str(environments_file))
200
-
204
+
201
205
  # Validate specific environment if requested
202
206
  if required_environment:
203
- validate_environments_config(
204
- environments_config,
205
- required_environments=[required_environment]
206
- )
207
-
207
+ validate_environments_config(environments_config, required_environments=[required_environment])
208
+
208
209
  return manifest_path, environments_config
209
210
 
210
211
 
211
212
  def generate_helpful_error_message(error: Exception, context: str = "") -> str:
212
213
  """
213
214
  Generate helpful error message with troubleshooting tips.
214
-
215
+
215
216
  Args:
216
217
  error: The original exception
217
218
  context: Additional context about where the error occurred
218
-
219
+
219
220
  Returns:
220
221
  Formatted error message with troubleshooting tips
221
222
  """
222
223
  base_msg = str(error)
223
-
224
+
224
225
  if context:
225
226
  base_msg = f"{context}: {base_msg}"
226
-
227
+
227
228
  # Add troubleshooting tips based on error type
228
229
  if isinstance(error, FileNotFoundError):
229
230
  if "environments.yaml" in base_msg:
@@ -246,5 +247,5 @@ def generate_helpful_error_message(error: Exception, context: str = "") -> str:
246
247
  "- Include team and environment (e.g., 'team-dev-agent')\n"
247
248
  "- Keep under 63 characters"
248
249
  )
249
-
250
+
250
251
  return base_msg
agentex/types/__init__.py CHANGED
@@ -62,9 +62,9 @@ from .agent_rpc_by_name_params import AgentRpcByNameParams as AgentRpcByNamePara
62
62
  from .task_message_content_param import TaskMessageContentParam as TaskMessageContentParam
63
63
  from .tool_request_content_param import ToolRequestContentParam as ToolRequestContentParam
64
64
  from .tool_response_content_param import ToolResponseContentParam as ToolResponseContentParam
65
- from .deployment_history_list_params import DeploymentHistoryListParams as DeploymentHistoryListParams
66
- from .deployment_history_list_response import DeploymentHistoryListResponse as DeploymentHistoryListResponse
67
65
  from .task_retrieve_by_name_params import TaskRetrieveByNameParams as TaskRetrieveByNameParams
68
66
  from .message_list_paginated_params import MessageListPaginatedParams as MessageListPaginatedParams
67
+ from .deployment_history_list_params import DeploymentHistoryListParams as DeploymentHistoryListParams
69
68
  from .task_retrieve_by_name_response import TaskRetrieveByNameResponse as TaskRetrieveByNameResponse
70
69
  from .message_list_paginated_response import MessageListPaginatedResponse as MessageListPaginatedResponse
70
+ from .deployment_history_list_response import DeploymentHistoryListResponse as DeploymentHistoryListResponse
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: agentex-sdk
3
- Version: 0.8.0
3
+ Version: 0.8.2
4
4
  Summary: The official Python library for the agentex API
5
5
  Project-URL: Homepage, https://github.com/scaleapi/scale-agentex-python
6
6
  Project-URL: Repository, https://github.com/scaleapi/scale-agentex-python
@@ -1,6 +1,6 @@
1
1
  agentex/__init__.py,sha256=TvS8DtvGAnubcoUjYIsuCpBzpsdpxBaJCS76s-l-PRo,2712
2
2
  agentex/_base_client.py,sha256=bYjTh_wPlo7zWeUO4hnrh30DrWsCAAVdohUlt-zsqUg,67248
3
- agentex/_client.py,sha256=WJM1kkmccMO8RNEuqUgxMyTcHXsIxVh3dOENIo0jXWw,26659
3
+ agentex/_client.py,sha256=39ydvoIqEPVYkM891ZMhCMKrbXR59QUxuKTtrTJGgMM,28482
4
4
  agentex/_compat.py,sha256=DQBVORjFb33zch24jzkhM14msvnzY7mmSmgDLaVFUM8,6562
5
5
  agentex/_constants.py,sha256=oGldMuFz7eZtwD8_6rJUippKhZB5fGSA7ffbCDGourA,466
6
6
  agentex/_exceptions.py,sha256=B09aFjWFRSShb9BFJd-MNDblsGDyGk3w-vItYmjg_AI,3222
@@ -11,7 +11,7 @@ agentex/_resource.py,sha256=S1t7wmR5WUvoDIhZjo_x-E7uoTJBynJ3d8tPJMQYdjw,1106
11
11
  agentex/_response.py,sha256=Tb9zazsnemO2rTxWtBjAD5WBqlhli5ZaXGbiKgdu5DE,28794
12
12
  agentex/_streaming.py,sha256=aOvLOte7kaclPGm-D0iNdM3uRcWrZ-T2B8t9BDNmpuA,10225
13
13
  agentex/_types.py,sha256=00q2kgDxUPJC16dU-YIUeOFsN5MzNW0zjzVXMlBYGV0,7296
14
- agentex/_version.py,sha256=jYEKs7ucbzh9GPs80L_jA7UO51E0BafioisYgZPYL5g,159
14
+ agentex/_version.py,sha256=N-oaYLvjQ5A1ZPup7gO50SUO8xLBkfetnHauya_5gcI,159
15
15
  agentex/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  agentex/_utils/__init__.py,sha256=7fch0GT9zpNnErbciSpUNa-SjTxxjY6kxHxKMOM4AGs,2305
17
17
  agentex/_utils/_compat.py,sha256=D8gtAvjJQrDWt9upS0XaG9Rr5l1QhiAx_I_1utT_tt0,1195
@@ -52,7 +52,7 @@ agentex/lib/adk/utils/_modules/client.py,sha256=UYSTThuZoX3nqF8iuRmRI8uPO00NrYgC
52
52
  agentex/lib/adk/utils/_modules/templating.py,sha256=tSiJGoDrF-XkMEi4MB_wVH6nyKyhSQwVZ8krN5R-86Q,3598
53
53
  agentex/lib/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
54
  agentex/lib/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
- agentex/lib/cli/commands/agents.py,sha256=E6O7p3B3P4yX-oYWRtzfbEyI5Pws-yzFi-htYJ7B_kY,14079
55
+ agentex/lib/cli/commands/agents.py,sha256=-zNA8TQnWW4UjLDjuZ6LNHxJqbdYOfGDn2-xX-4xWFM,13516
56
56
  agentex/lib/cli/commands/init.py,sha256=CrKksw5l1w9hXSdDN6wPNgQlyE1k_kqHlA6QrOenqHM,15537
57
57
  agentex/lib/cli/commands/main.py,sha256=QWychw-Xq3nQ00BMypgOEF4w1WauNrgQ06PDardNP_8,1042
58
58
  agentex/lib/cli/commands/secrets.py,sha256=t4zvoyjOHmw-8qp6nsxdZCvqy43QXOl0MWXA3ooBO6Q,5640
@@ -215,10 +215,10 @@ agentex/lib/sdk/config/agent_config.py,sha256=4q1qMdE9_gBTsLB_NplY4exzUYLd8wQIyw
215
215
  agentex/lib/sdk/config/agent_manifest.py,sha256=28e2o6yDe_hQMfegMUh07w-B5Kh9WZHv0z77kJvcJFk,8626
216
216
  agentex/lib/sdk/config/build_config.py,sha256=pTgtvecHQejp4SlCvS6f6TfGnWqXFG-Ac0L8zthvyGk,1085
217
217
  agentex/lib/sdk/config/deployment_config.py,sha256=QZ8zsYt4Uchw1hCs2y9mHJ8EPVBbbOfmBktv1SC_4K4,4028
218
- agentex/lib/sdk/config/environment_config.py,sha256=RrQPMHNAKOuqGpXPDKoQNi50lXssLOncIYLy4iwbrxs,6790
218
+ agentex/lib/sdk/config/environment_config.py,sha256=zI-U060RsZZf8G6AAh1Y9F0Ls2AQIy0-Jn98NQT2yP8,8904
219
219
  agentex/lib/sdk/config/local_development_config.py,sha256=Sx7Cf3bP4oVjavdqy5rx4pvLZocyS19uJcrEj_mA5Nc,1733
220
220
  agentex/lib/sdk/config/project_config.py,sha256=uMrg9BqEQFcnqdlqqSLYsaQkP1mMedhEZZMzPBSyCK0,3698
221
- agentex/lib/sdk/config/validation.py,sha256=ox8g2vwjYsmfNcz4G-sbPw0ccWjylJRG5bufTEPQMCk,9024
221
+ agentex/lib/sdk/config/validation.py,sha256=yzt9n7KTwHotA-vExa_LK_D_mgq4FCDGIjHuKSgs0Ks,9319
222
222
  agentex/lib/sdk/fastacp/__init__.py,sha256=UvAdexdnfb4z0F4a2sfXROFyh9EjH89kf3AxHPybzCM,75
223
223
  agentex/lib/sdk/fastacp/fastacp.py,sha256=3aT74pFwF76VoTbQnGZsF6As42aLa2o_JrO6EP_XHQM,4591
224
224
  agentex/lib/sdk/fastacp/base/base_acp_server.py,sha256=W2rMZUC-5GLvLJsLFKZHtmyG9Uhrsgffqo9qcomThsQ,17163
@@ -278,7 +278,7 @@ agentex/resources/tracker.py,sha256=bxRuzI9ThHrZtJbZoctU9vodE1kxAqcdlwQVrcbQTb4,
278
278
  agentex/resources/messages/__init__.py,sha256=_J1eusFtr_k6zrAntJSuqx6LWEUBSTrV1OZZh7MaDPE,1015
279
279
  agentex/resources/messages/batch.py,sha256=bYDIf0ZF3-sTKnGfFmzFQUn8LMtMYoniY977J3zr8q8,9653
280
280
  agentex/resources/messages/messages.py,sha256=YFmyAHMNCoAyLMi8ckSJD0cYQ9_ccf24ohUhu9K9VC8,105228
281
- agentex/types/__init__.py,sha256=p14jAyCFSwh8rtQQhA-6Kpf28G77wYIQFCfUPUfwyx8,4725
281
+ agentex/types/__init__.py,sha256=8Hg4rMRoOY8k9Vl69Hr9bzb1U_tWNw9DNQtUarLXx18,4725
282
282
  agentex/types/acp_type.py,sha256=lEn_w4z-RIgyUVTQr8mm5l9OdFDQMDclbJU_lKa4Mi8,217
283
283
  agentex/types/agent.py,sha256=hwgmtylJYezzmGJbzbBQ7sn3oV2_bCZqgqlNq9WpZ0g,1318
284
284
  agentex/types/agent_list_params.py,sha256=s4heb3MQwMprmuol_smUDL2N0u-5WUbUCxt3J5Dqnag,515
@@ -351,8 +351,8 @@ agentex/types/messages/batch_update_params.py,sha256=Ug5CThbD49a8j4qucg04OdmVrp_
351
351
  agentex/types/messages/batch_update_response.py,sha256=TbSBe6SuPzjXXWSj-nRjT1JHGBooTshHQQDa1AixQA8,278
352
352
  agentex/types/shared/__init__.py,sha256=IKs-Qn5Yja0kFh1G1kDqYZo43qrOu1hSoxlPdN-85dI,149
353
353
  agentex/types/shared/delete_response.py,sha256=8qH3zvQXaOHYQSHyXi7UQxdR4miTzR7V9K4zXVsiUyk,215
354
- agentex_sdk-0.8.0.dist-info/METADATA,sha256=J-CzixLxEoZQeJ35VgioZHh8V0c5a7Z5ThKIVRw-yTg,15553
355
- agentex_sdk-0.8.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
356
- agentex_sdk-0.8.0.dist-info/entry_points.txt,sha256=V7vJuMZdF0UlvgX6KiBN7XUvq_cxF5kplcYvc1QlFaQ,62
357
- agentex_sdk-0.8.0.dist-info/licenses/LICENSE,sha256=Q1AOx2FtRcMlyMgQJ9eVN2WKPq2mQ33lnB4tvWxabLA,11337
358
- agentex_sdk-0.8.0.dist-info/RECORD,,
354
+ agentex_sdk-0.8.2.dist-info/METADATA,sha256=MOVWQExMpCFpUtxRwBGaOJe8dccARQtBC9NNc9nkSp8,15553
355
+ agentex_sdk-0.8.2.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
356
+ agentex_sdk-0.8.2.dist-info/entry_points.txt,sha256=V7vJuMZdF0UlvgX6KiBN7XUvq_cxF5kplcYvc1QlFaQ,62
357
+ agentex_sdk-0.8.2.dist-info/licenses/LICENSE,sha256=Eejl902yry8m7Bl6gRYRUObLifIrC61CmV369C6-dqQ,11337
358
+ agentex_sdk-0.8.2.dist-info/RECORD,,
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright 2025 Agentex
189
+ Copyright 2026 Agentex
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.