agenticstar-platform 3.2.1__tar.gz → 3.2.3__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 (146) hide show
  1. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/PKG-INFO +40 -5
  2. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/README.md +39 -4
  3. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/__init__.py +1 -1
  4. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/events/handlers.py +37 -4
  5. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/runner.py +3 -1
  6. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/storage/base.py +51 -7
  7. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/storage/s3.py +21 -8
  8. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/pyproject.toml +1 -1
  9. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/events/test_handler_type_routing.py +60 -3
  10. agenticstar_platform-3.2.3/tests/events/test_notify_token.py +101 -0
  11. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/runner/test_marketplace_runner_started_fence.py +21 -0
  12. agenticstar_platform-3.2.3/tests/storage/test_s3_credential.py +178 -0
  13. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/LICENSE +0 -0
  14. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/_config_utils.py +0 -0
  15. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/audit/ROW_HASH_SPEC.md +0 -0
  16. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/audit/__init__.py +0 -0
  17. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/audit/action_audit.py +0 -0
  18. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/audit/guardrail_alerts.py +0 -0
  19. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/audit/row_hash_fixture.json +0 -0
  20. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/auth/__init__.py +0 -0
  21. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/auth/client.py +0 -0
  22. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/auth/config.py +0 -0
  23. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/auth/exceptions.py +0 -0
  24. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/auth/models.py +0 -0
  25. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/common/__init__.py +0 -0
  26. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/common/context_manager.py +0 -0
  27. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/common/secrets.py +0 -0
  28. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/common/validation.py +0 -0
  29. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/__init__.py +0 -0
  30. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/api_manager.py +0 -0
  31. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/completion_report.py +0 -0
  32. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/config.py +0 -0
  33. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/config_access.py +0 -0
  34. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/data_access.py +0 -0
  35. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/execution_access.py +0 -0
  36. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/factory.py +0 -0
  37. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/manager.py +0 -0
  38. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/pod_runtime.py +0 -0
  39. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/started_fence.py +0 -0
  40. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/telemetry_access.py +0 -0
  41. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/db/wire_types.py +0 -0
  42. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/events/__init__.py +0 -0
  43. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/events/emitter.py +0 -0
  44. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/events/models.py +0 -0
  45. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/events/types.py +0 -0
  46. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/README.md +0 -0
  47. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/__init__.py +0 -0
  48. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/__main__.py +0 -0
  49. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/checks.py +0 -0
  50. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/docker-compose.yml +0 -0
  51. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/doctor.py +0 -0
  52. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/lab_config.py +0 -0
  53. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/offline_embedding.py +0 -0
  54. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/run_lab.py +0 -0
  55. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/seed.py +0 -0
  56. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/memory/__init__.py +0 -0
  57. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/memory/semantic.py +0 -0
  58. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/metering/__init__.py +0 -0
  59. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/metering/meter.py +0 -0
  60. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/policy/__init__.py +0 -0
  61. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/policy/internal_operations.py +0 -0
  62. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/policy/model.py +0 -0
  63. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/rag/__init__.py +0 -0
  64. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/rag/base.py +0 -0
  65. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/rag/embedding.py +0 -0
  66. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/rag/qdrant_manager.py +0 -0
  67. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/runtime.py +0 -0
  68. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/security/__init__.py +0 -0
  69. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/security/aws.py +0 -0
  70. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/security/azure.py +0 -0
  71. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/security/base.py +0 -0
  72. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/security/content_safety_validator.py +0 -0
  73. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/security/factory.py +0 -0
  74. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/security/gcp.py +0 -0
  75. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/storage/__init__.py +0 -0
  76. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/storage/azure.py +0 -0
  77. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/storage/gcs.py +0 -0
  78. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/storage/paths.py +0 -0
  79. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/__init__.py +0 -0
  80. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/canonical.py +0 -0
  81. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/child.py +0 -0
  82. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/context.py +0 -0
  83. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/envelope.py +0 -0
  84. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/errors.py +0 -0
  85. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/files.py +0 -0
  86. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/runner.py +0 -0
  87. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/runtime_client.py +0 -0
  88. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/schema.py +0 -0
  89. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/testing.py +0 -0
  90. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/docs/agenticstar_platform.html +0 -0
  91. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/docs/examples/workflow_mock_send_agent.py +0 -0
  92. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/docs/examples/workflow_reply_agent.py +0 -0
  93. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/docs/index.html +0 -0
  94. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/docs/search.js +0 -0
  95. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/__init__.py +0 -0
  96. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/audit/__init__.py +0 -0
  97. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/audit/test_action_audit_30.py +0 -0
  98. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/audit/test_row_hash_fixture.py +0 -0
  99. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/auth/__init__.py +0 -0
  100. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/auth/test_auth_type_bearer_only.py +0 -0
  101. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/auth/test_get_user_path_encoding.py +0 -0
  102. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/auth/test_mcp_tokens_expiry.py +0 -0
  103. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/auth/test_mcp_tokens_rejected.py +0 -0
  104. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/db/__init__.py +0 -0
  105. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/db/test_api_manager_drain_and_headers.py +0 -0
  106. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/db/test_completion_report.py +0 -0
  107. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/db/test_data_access_identifier_validation.py +0 -0
  108. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/db/test_manager_execute_query_dispatch.py +0 -0
  109. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/db/test_telemetry_tools_used_contract.py +0 -0
  110. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/db/test_wire_param_types.py +0 -0
  111. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/events/__init__.py +0 -0
  112. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/lab/test_lab_contract.py +0 -0
  113. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/lab/test_lab_docker.py +0 -0
  114. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/memory/__init__.py +0 -0
  115. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/memory/test_semantic_converter_base_url.py +0 -0
  116. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/metering/__init__.py +0 -0
  117. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/metering/test_meter_cache_split.py +0 -0
  118. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/policy/__init__.py +0 -0
  119. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/policy/test_a2a_connection_rule.py +0 -0
  120. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/policy/test_decision_layer.py +0 -0
  121. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/policy/test_evaluate_decision_table.py +0 -0
  122. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/policy/test_policy_model.py +0 -0
  123. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/policy/test_scoped_forbid.py +0 -0
  124. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/rag/__init__.py +0 -0
  125. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/rag/test_embedding_dimensions.py +0 -0
  126. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/rag/test_qdrant_error_hierarchy.py +0 -0
  127. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/rag/test_qdrant_get_statistics.py +0 -0
  128. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/runner/__init__.py +0 -0
  129. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/runner/test_marketplace_runner_contract.py +0 -0
  130. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/runner/test_marketplace_runner_db_fallback.py +0 -0
  131. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/security/__init__.py +0 -0
  132. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/security/test_azure_connect_retry.py +0 -0
  133. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/security/test_azure_prompt_shield_documents.py +0 -0
  134. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/security/test_factory_vertex_ai_safety.py +0 -0
  135. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/security/test_gcp_vertex_ai_safety.py +0 -0
  136. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/storage/__init__.py +0 -0
  137. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/storage/test_azure_credential.py +0 -0
  138. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/test_lazy_import_contract.py +0 -0
  139. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/test_readme_quickstart.py +0 -0
  140. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/test_storage_provider_extra_contract.py +0 -0
  141. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/workflow/__init__.py +0 -0
  142. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/workflow/agent_fixtures.py +0 -0
  143. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/workflow/test_child_failure_summary.py +0 -0
  144. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/workflow/test_workflow_incomplete.py +0 -0
  145. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/workflow/test_workflow_runner.py +0 -0
  146. {agenticstar_platform-3.2.1 → agenticstar_platform-3.2.3}/tests/workflow/test_workflow_testing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agenticstar-platform
3
- Version: 3.2.1
3
+ Version: 3.2.3
4
4
  Summary: AGENTICSTAR Platform SDK - Enterprise AI Agent Infrastructure
5
5
  Project-URL: Homepage, https://developers.fd.agenticstar.tm.softbank.jp/
6
6
  Project-URL: Documentation, https://developers.fd.agenticstar.tm.softbank.jp/
@@ -224,6 +224,7 @@ Environment variable contract:
224
224
  | `REQUEST_SOURCE` / `AGENT_ID` | Same as above (optional) | — |
225
225
  | `DB_HOST` / `DB_PORT` / `DB_DATABASE` / `DB_USER` / `DB_PASSWORD` | Env var settings at agent registration (`PostgreSQLConfig.from_env()` contract) | ✅ |
226
226
  | `WEBHOOK_URL` | Env var settings at agent registration | ✅ |
227
+ | `ASTER_NOTIFY_TOKEN` | Injected by the Marketplace executor for each execution (SDK >= 3.2.2 sends it as `Authorization: Bearer` on the webhook notification) | — |
227
228
 
228
229
  Terminal event rules:
229
230
 
@@ -235,7 +236,8 @@ Terminal event rules:
235
236
  If you need the low-level API (to compose handlers yourself), pass
236
237
  `create_marketplace_handler(data_access, webhook_url, user_id, conversation_id, message_id)`
237
238
  (a composite handler for DB persistence + webhook notification; requires the `[db]` + `[webhook]`
238
- extras) directly to `EventEmitter`, as before.
239
+ extras) directly to `EventEmitter`, as before. Without a `token_provider`, it sends the
240
+ `ASTER_NOTIFY_TOKEN` env var as `Authorization: Bearer` when it is set (SDK >= 3.2.2).
239
241
 
240
242
  ### 2.4 Report completion for a cycle execution (SDK >= 3.0.14, `mp-chat/1`)
241
243
 
@@ -471,6 +473,24 @@ config = AzureBlobConfig.from_dict({
471
473
  client = AzureBlobStorageClient(config)
472
474
  ```
473
475
 
476
+ S3 without access keys (3.2.3): leave `aws_access_key_id` and `aws_secret_access_key` both out (or
477
+ empty). The client then passes no keys and lets boto3 find the credentials with its default
478
+ credential chain (for example IRSA on EKS, environment variables, `~/.aws` files, EKS Pod Identity
479
+ or an EC2 instance role; boto3 decides which one is used first). This is what an EKS Pod with IRSA
480
+ needs; do not also give that Pod `AWS_ACCESS_KEY_ID` in its env, which boto3 checks before IRSA.
481
+ The role needs S3 access to the bucket. Give both keys or neither: only one of them raises
482
+ `StorageConfigError`.
483
+
484
+ ```python
485
+ from agenticstar_platform.storage import S3StorageClient, S3Config
486
+
487
+ config = S3Config.from_dict({
488
+ "bucket_name": "your-bucket",
489
+ "region_name": "us-east-1",
490
+ })
491
+ client = S3StorageClient(config)
492
+ ```
493
+
474
494
  ### Telemetry / LLM Usage Tracking (Marketplace)
475
495
 
476
496
  `TelemetryAccess` (under `db` module) writes records to the `ai_telemetry` table. **Unknown fields are stored in the `metadata` jsonb column automatically** — no schema migration is required to add new tracking dimensions.
@@ -734,8 +754,9 @@ async def run(context):
734
754
 
735
755
  **Ending a step as not completed** (SDK ≥ 3.2.1). When your agent cannot complete the step and running it again would
736
756
  give the same result — the instructions are missing, a setting is wrong — call `context.incomplete(reason, comment)` and
737
- return. The platform does not retry the step automatically; it asks a person to check it and shows the reason and the
738
- comment in the execution record.
757
+ return. The platform does not retry the step automatically; it asks a person to check it and shows the reason in the
758
+ execution record. A platform that reads the end-of-run report (below) also shows the comment there; Marketplace 3.2.0
759
+ does not show the comment.
739
760
 
740
761
  ```python
741
762
  async def run(context):
@@ -1107,6 +1128,20 @@ See the [developer portal](https://developers.fd.agenticstar.tm.softbank.jp/) fo
1107
1128
 
1108
1129
  ## Changelog
1109
1130
 
1131
+ ### 3.2.3 (2026-11-04) — storage: S3 without access keys (IRSA) / events: a failed run ends the chat message
1132
+
1133
+ 3.2.2 was not published to PyPI; 3.2.3 includes its change (the notify token below).
1134
+
1135
+ - **`S3StorageClient` works without access keys.** Leave `aws_access_key_id` and `aws_secret_access_key` both out (or empty) and the client passes no keys and lets boto3 find the credentials with its default credential chain (for example IRSA on EKS with `AWS_ROLE_ARN` + `AWS_WEB_IDENTITY_TOKEN_FILE`, environment variables, `~/.aws` files, EKS Pod Identity or an EC2 instance role; boto3 decides the order). Before, the client raised `StorageConfigError: S3 requires aws_access_key_id and aws_secret_access_key` on an EKS runner Pod that has only IRSA. Giving only one of the two keys still raises `StorageConfigError` (it is a configuration mistake, and the client does not silently fall back to other credentials). Both keys work as before, exactly as given (a non-blank key is not stripped); an empty or whitespace-only key, including a `SecretStr`, counts as not given. With no keys the client is created even when boto3 finds no credentials; the first call then fails (for example `object_exists()` returns `False`). `S3Config` keeps its field order, so positional construction is unchanged; `S3Config.from_dict` no longer requires the two keys.
1136
+ - **A failed run ends the chat message.** `create_marketplace_handler` (and so `run_marketplace_agent`) now sends `COMPLETION_FAILURE` with `finish_reason: "stop"`, the same as the platform's own agent worker. The chat front ends a message only on `"stop"`; with the previous `"error"` the failure text was added but the message kept showing as in progress. The failure is told by the message text, as before. `WebhookEventHandler` used on its own keeps `"error"` by default, so a receiver of your own that tells failures by `"error"` is unchanged; the new argument `failure_finish_reason="stop"` sends what the chat front expects. If you pointed `create_marketplace_handler` at a receiver of your own that needs `"error"`, build `WebhookEventHandler` yourself instead.
1137
+ - docs: "Ending a step as not completed" now says that the execution record shows the reason, and that the comment is shown only by a platform that reads the end-of-run report (Marketplace 3.2.0 does not show it). In 3.2.1 and 3.2.2 that section said the record shows the comment. The SDK's behavior is unchanged.
1138
+
1139
+ ### 3.2.2 (2026-10-30) — events: the webhook notification carries the execution's notify token (`ASTER_NOTIFY_TOKEN`)
1140
+
1141
+ - **`create_marketplace_handler(...)` sends `Authorization: Bearer <token>` on the webhook notification when the `ASTER_NOTIFY_TOKEN` env var is set.** The Marketplace executor issues this token for each execution and passes it to the agent Pod; the platform checks it when it receives the notification. Without a `token_provider` argument, the handler reads the env var each time it sends. When the env var is missing or empty, no `Authorization` header is sent, as before. A `token_provider` you pass is used as before, and the env var is not read. `run_marketplace_agent` uses this default, so a runner agent sends the token without code changes.
1142
+ - `WebhookEventHandler` used on its own does not read `ASTER_NOTIFY_TOKEN`; as before, it sends an `Authorization` header only when you give one (through its `token_provider` or `headers`).
1143
+ - To use it, rebuild the agent image with this SDK.
1144
+
1110
1145
  ### 3.2.1 (2026-10-28) — workflow: end a step as not completed (`context.incomplete`) with an end-of-run report / a fake runtime for testing (`agenticstar_platform.workflow.testing`)
1111
1146
 
1112
1147
  - **New `WorkflowContext.incomplete(reason, comment=None)`.** An agent that cannot complete its step, where running it again would give the same result (missing instructions, a wrong setting), ends the step as `incomplete` with `reason` as the reason code. The platform does not retry it automatically and asks a person to check it. `reason` follows the rules of `report_completion()` (lowercase snake_case of at most 32 characters; any other value becomes `incomplete` with a warning); `comment` is cut at 2,048 UTF-16 code units. The declaration comes after an audit persistence failure, cancellation, the deadline, the execution being closed elsewhere, a pod stop signal and a denied tool call, and before an exception (an exception after the declaration still ends as `incomplete`), an invalid output, a skip and a missing required output. A second declaration is not recorded. See "Ending a step as not completed" in the Workflow Runner section.
@@ -1553,4 +1588,4 @@ Marketplace SDK reliability fixes (surfaced while building agents from the guide
1553
1588
 
1554
1589
  ## Version
1555
1590
 
1556
- 3.2.1
1591
+ 3.2.3
@@ -143,6 +143,7 @@ Environment variable contract:
143
143
  | `REQUEST_SOURCE` / `AGENT_ID` | Same as above (optional) | — |
144
144
  | `DB_HOST` / `DB_PORT` / `DB_DATABASE` / `DB_USER` / `DB_PASSWORD` | Env var settings at agent registration (`PostgreSQLConfig.from_env()` contract) | ✅ |
145
145
  | `WEBHOOK_URL` | Env var settings at agent registration | ✅ |
146
+ | `ASTER_NOTIFY_TOKEN` | Injected by the Marketplace executor for each execution (SDK >= 3.2.2 sends it as `Authorization: Bearer` on the webhook notification) | — |
146
147
 
147
148
  Terminal event rules:
148
149
 
@@ -154,7 +155,8 @@ Terminal event rules:
154
155
  If you need the low-level API (to compose handlers yourself), pass
155
156
  `create_marketplace_handler(data_access, webhook_url, user_id, conversation_id, message_id)`
156
157
  (a composite handler for DB persistence + webhook notification; requires the `[db]` + `[webhook]`
157
- extras) directly to `EventEmitter`, as before.
158
+ extras) directly to `EventEmitter`, as before. Without a `token_provider`, it sends the
159
+ `ASTER_NOTIFY_TOKEN` env var as `Authorization: Bearer` when it is set (SDK >= 3.2.2).
158
160
 
159
161
  ### 2.4 Report completion for a cycle execution (SDK >= 3.0.14, `mp-chat/1`)
160
162
 
@@ -390,6 +392,24 @@ config = AzureBlobConfig.from_dict({
390
392
  client = AzureBlobStorageClient(config)
391
393
  ```
392
394
 
395
+ S3 without access keys (3.2.3): leave `aws_access_key_id` and `aws_secret_access_key` both out (or
396
+ empty). The client then passes no keys and lets boto3 find the credentials with its default
397
+ credential chain (for example IRSA on EKS, environment variables, `~/.aws` files, EKS Pod Identity
398
+ or an EC2 instance role; boto3 decides which one is used first). This is what an EKS Pod with IRSA
399
+ needs; do not also give that Pod `AWS_ACCESS_KEY_ID` in its env, which boto3 checks before IRSA.
400
+ The role needs S3 access to the bucket. Give both keys or neither: only one of them raises
401
+ `StorageConfigError`.
402
+
403
+ ```python
404
+ from agenticstar_platform.storage import S3StorageClient, S3Config
405
+
406
+ config = S3Config.from_dict({
407
+ "bucket_name": "your-bucket",
408
+ "region_name": "us-east-1",
409
+ })
410
+ client = S3StorageClient(config)
411
+ ```
412
+
393
413
  ### Telemetry / LLM Usage Tracking (Marketplace)
394
414
 
395
415
  `TelemetryAccess` (under `db` module) writes records to the `ai_telemetry` table. **Unknown fields are stored in the `metadata` jsonb column automatically** — no schema migration is required to add new tracking dimensions.
@@ -653,8 +673,9 @@ async def run(context):
653
673
 
654
674
  **Ending a step as not completed** (SDK ≥ 3.2.1). When your agent cannot complete the step and running it again would
655
675
  give the same result — the instructions are missing, a setting is wrong — call `context.incomplete(reason, comment)` and
656
- return. The platform does not retry the step automatically; it asks a person to check it and shows the reason and the
657
- comment in the execution record.
676
+ return. The platform does not retry the step automatically; it asks a person to check it and shows the reason in the
677
+ execution record. A platform that reads the end-of-run report (below) also shows the comment there; Marketplace 3.2.0
678
+ does not show the comment.
658
679
 
659
680
  ```python
660
681
  async def run(context):
@@ -1026,6 +1047,20 @@ See the [developer portal](https://developers.fd.agenticstar.tm.softbank.jp/) fo
1026
1047
 
1027
1048
  ## Changelog
1028
1049
 
1050
+ ### 3.2.3 (2026-11-04) — storage: S3 without access keys (IRSA) / events: a failed run ends the chat message
1051
+
1052
+ 3.2.2 was not published to PyPI; 3.2.3 includes its change (the notify token below).
1053
+
1054
+ - **`S3StorageClient` works without access keys.** Leave `aws_access_key_id` and `aws_secret_access_key` both out (or empty) and the client passes no keys and lets boto3 find the credentials with its default credential chain (for example IRSA on EKS with `AWS_ROLE_ARN` + `AWS_WEB_IDENTITY_TOKEN_FILE`, environment variables, `~/.aws` files, EKS Pod Identity or an EC2 instance role; boto3 decides the order). Before, the client raised `StorageConfigError: S3 requires aws_access_key_id and aws_secret_access_key` on an EKS runner Pod that has only IRSA. Giving only one of the two keys still raises `StorageConfigError` (it is a configuration mistake, and the client does not silently fall back to other credentials). Both keys work as before, exactly as given (a non-blank key is not stripped); an empty or whitespace-only key, including a `SecretStr`, counts as not given. With no keys the client is created even when boto3 finds no credentials; the first call then fails (for example `object_exists()` returns `False`). `S3Config` keeps its field order, so positional construction is unchanged; `S3Config.from_dict` no longer requires the two keys.
1055
+ - **A failed run ends the chat message.** `create_marketplace_handler` (and so `run_marketplace_agent`) now sends `COMPLETION_FAILURE` with `finish_reason: "stop"`, the same as the platform's own agent worker. The chat front ends a message only on `"stop"`; with the previous `"error"` the failure text was added but the message kept showing as in progress. The failure is told by the message text, as before. `WebhookEventHandler` used on its own keeps `"error"` by default, so a receiver of your own that tells failures by `"error"` is unchanged; the new argument `failure_finish_reason="stop"` sends what the chat front expects. If you pointed `create_marketplace_handler` at a receiver of your own that needs `"error"`, build `WebhookEventHandler` yourself instead.
1056
+ - docs: "Ending a step as not completed" now says that the execution record shows the reason, and that the comment is shown only by a platform that reads the end-of-run report (Marketplace 3.2.0 does not show it). In 3.2.1 and 3.2.2 that section said the record shows the comment. The SDK's behavior is unchanged.
1057
+
1058
+ ### 3.2.2 (2026-10-30) — events: the webhook notification carries the execution's notify token (`ASTER_NOTIFY_TOKEN`)
1059
+
1060
+ - **`create_marketplace_handler(...)` sends `Authorization: Bearer <token>` on the webhook notification when the `ASTER_NOTIFY_TOKEN` env var is set.** The Marketplace executor issues this token for each execution and passes it to the agent Pod; the platform checks it when it receives the notification. Without a `token_provider` argument, the handler reads the env var each time it sends. When the env var is missing or empty, no `Authorization` header is sent, as before. A `token_provider` you pass is used as before, and the env var is not read. `run_marketplace_agent` uses this default, so a runner agent sends the token without code changes.
1061
+ - `WebhookEventHandler` used on its own does not read `ASTER_NOTIFY_TOKEN`; as before, it sends an `Authorization` header only when you give one (through its `token_provider` or `headers`).
1062
+ - To use it, rebuild the agent image with this SDK.
1063
+
1029
1064
  ### 3.2.1 (2026-10-28) — workflow: end a step as not completed (`context.incomplete`) with an end-of-run report / a fake runtime for testing (`agenticstar_platform.workflow.testing`)
1030
1065
 
1031
1066
  - **New `WorkflowContext.incomplete(reason, comment=None)`.** An agent that cannot complete its step, where running it again would give the same result (missing instructions, a wrong setting), ends the step as `incomplete` with `reason` as the reason code. The platform does not retry it automatically and asks a person to check it. `reason` follows the rules of `report_completion()` (lowercase snake_case of at most 32 characters; any other value becomes `incomplete` with a warning); `comment` is cut at 2,048 UTF-16 code units. The declaration comes after an audit persistence failure, cancellation, the deadline, the execution being closed elsewhere, a pod stop signal and a denied tool call, and before an exception (an exception after the declaration still ends as `incomplete`), an invalid output, a skip and a missing required output. A second declaration is not recorded. See "Ending a step as not completed" in the Workflow Runner section.
@@ -1472,4 +1507,4 @@ Marketplace SDK reliability fixes (surfaced while building agents from the guide
1472
1507
 
1473
1508
  ## Version
1474
1509
 
1475
- 3.2.1
1510
+ 3.2.3
@@ -9,7 +9,7 @@ import importlib
9
9
  import importlib.util
10
10
  from typing import TYPE_CHECKING
11
11
 
12
- __version__ = "3.2.1"
12
+ __version__ = "3.2.3"
13
13
 
14
14
  # Common utilities are internal - not exported as public API
15
15
  # Use agenticstar_platform.common for internal SDK development only
@@ -33,6 +33,7 @@ Example:
33
33
  import asyncio
34
34
  import json
35
35
  import logging
36
+ import os
36
37
  from typing import Any, Callable, Dict, List, Optional
37
38
 
38
39
  from .types import EventType
@@ -249,6 +250,7 @@ class WebhookEventHandler:
249
250
  chunk_type_map: Optional[Dict[EventType, str]] = None,
250
251
  request_source: Optional[str] = None,
251
252
  message_type_map: Optional[Dict[EventType, str]] = None,
253
+ failure_finish_reason: str = "error",
252
254
  ):
253
255
  """
254
256
  WebhookEventHandlerを初期化
@@ -269,6 +271,10 @@ class WebhookEventHandler:
269
271
  message_type_map: EventType→トップレベルtypeのカスタムマッピング
270
272
  (オプション)。省略時は HITL系→"vnc_hitl_event"、
271
273
  PROGRESS_MESSAGE→"progress_message" を切り替えるデフォルトを使用。
274
+ failure_finish_reason: COMPLETION_FAILURE の finish_reason(既定 "error"。3.2.3)。
275
+ AgenticStar のチャット画面(chatboardfront の監視)へ送るときは "stop" にする:
276
+ 受信側は "stop" のときだけメッセージを終わらせ、"error" は読まない(本体の worker も
277
+ 失敗を "stop" で送る)。create_marketplace_handler はこれを "stop" にして作る
272
278
 
273
279
  Example:
274
280
  >>> handler = WebhookEventHandler(
@@ -309,10 +315,10 @@ class WebhookEventHandler:
309
315
  EventType.FINAL_RESULT: None,
310
316
  }
311
317
 
312
- # finish_reasonマッピング
318
+ # finish_reasonマッピング(失敗の値は failure_finish_reason。既定 "error" は従来どおり)
313
319
  self._finish_reason_map = {
314
320
  EventType.COMPLETION_SUCCESS: "stop",
315
- EventType.COMPLETION_FAILURE: "error",
321
+ EventType.COMPLETION_FAILURE: failure_finish_reason,
316
322
  }
317
323
 
318
324
  logger.debug(
@@ -502,6 +508,20 @@ class CompositeEventHandler:
502
508
  return None
503
509
 
504
510
 
511
+ # 完了の知らせ(/webhook/notify)に付ける実行ごとのトークンの env。
512
+ # Marketplace executor が実行ごとに発行し、会話の Pod に渡す。受信側(front の監視)が
513
+ # Authorization: Bearer で確かめる。
514
+ NOTIFY_TOKEN_ENV = "ASTER_NOTIFY_TOKEN"
515
+
516
+
517
+ def _notify_token_from_env() -> Optional[str]:
518
+ """env ASTER_NOTIFY_TOKEN から知らせのトークンを返す(無い・空なら None)
519
+
520
+ create_marketplace_handler の既定の token_provider。送るたびに env を読む。
521
+ """
522
+ return os.environ.get(NOTIFY_TOKEN_ENV) or None
523
+
524
+
505
525
  def create_marketplace_handler(
506
526
  data_access: Any,
507
527
  webhook_url: str,
@@ -522,12 +542,19 @@ def create_marketplace_handler(
522
542
  user_id: ユーザーID
523
543
  conversation_id: 会話ID
524
544
  message_id: メッセージID
525
- token_provider: 認証トークン取得関数(オプション)
545
+ token_provider: 認証トークン取得関数(オプション)。
546
+ 省略時(None)は env ASTER_NOTIFY_TOKEN を送るたびに読む既定の関数を使い、
547
+ 値があれば Webhook に Authorization: Bearer <値> を付ける。env が無い・空なら付けない
548
+ (Marketplace executor が実行ごとに発行して Pod に渡すトークン。呼び出し側のコードは
549
+ 変えなくてよい。SDK 3.2.2 から)。明示した場合はその関数だけを使い、env は読まない。
526
550
  request_source: リクエスト発信元("internal" / "external")。
527
551
  受信側で Progress SSE 配信 と 外部Webhook送信 の振り分けに使われる。
528
552
  外部API経由(extapi)からの呼び出しでは "external" を指定すること。
529
553
  通常はPod起動スクリプトで os.environ.get("REQUEST_SOURCE", "internal") を渡す。
530
554
 
555
+ 失敗の完了(COMPLETION_FAILURE)は finish_reason "stop" で送る(3.2.3。チャット画面は "stop" で
556
+ メッセージを終わらせる。WebhookEventHandler の既定 "error" とは違う)。
557
+
531
558
  Returns:
532
559
  CompositeEventHandler: DB + Webhookの複合ハンドラー
533
560
 
@@ -561,8 +588,14 @@ def create_marketplace_handler(
561
588
  webhook_url=webhook_url,
562
589
  conversation_id=conversation_id,
563
590
  message_id=message_id,
564
- token_provider=token_provider,
591
+ token_provider=(
592
+ token_provider if token_provider is not None else _notify_token_from_env
593
+ ),
565
594
  request_source=request_source,
595
+ # 失敗も "stop"(3.2.3): 受信側のチャット画面(chatboardfront の監視)は "stop" のときだけ
596
+ # メッセージを終わらせ、"error" は読まない。本体の worker も COMPLETION_FAILURE を "stop" で送る
597
+ # (src/subscribers/common.py の get_finish_reason)。失敗の中身は本文で伝わる
598
+ failure_finish_reason="stop",
566
599
  )
567
600
 
568
601
  return CompositeEventHandler([db_handler, webhook_handler])
@@ -67,7 +67,7 @@ from .db import DataAccess, PostgreSQLConfig, create_postgresql_manager
67
67
  from .db.execution_access import ExecutionAccess
68
68
  from .db.started_fence import StartedFenceTaken, take_started_fence
69
69
  from .events import EventEmitter, EventType
70
- from .events.handlers import create_marketplace_handler
70
+ from .events.handlers import NOTIFY_TOKEN_ENV, create_marketplace_handler
71
71
 
72
72
  logger = logging.getLogger(__name__)
73
73
 
@@ -315,6 +315,8 @@ async def arun_marketplace_agent(
315
315
  conversation_id=identity["CONVERSATION_ID"],
316
316
  message_id=identity["MESSAGE_ID"],
317
317
  request_source=request_source or env.get("REQUEST_SOURCE", "internal"),
318
+ # 完了の知らせの実行ごとのトークン(3.2.2)。identity・WEBHOOK_URL と同じく渡された env から読む
319
+ token_provider=lambda: env.get(NOTIFY_TOKEN_ENV) or None,
318
320
  )
319
321
 
320
322
  execution_id = identity["EXECUTION_ID"]
@@ -243,12 +243,33 @@ def _blank_to_none(value: Any) -> Optional[str]:
243
243
  return text if text else None
244
244
 
245
245
 
246
+ def _blank_key_to_none(value: Any) -> Any:
247
+ """鍵の値が None・空・空白だけなら None、それ以外は渡された値をそのまま返す。
248
+
249
+ ``_blank_to_none`` と違い、空でない値は strip しない (S3 互換のストレージの鍵の前後の空白も
250
+ 署名に使われる値の一部)。``SecretStr`` は中身で空かどうかを見て、空でなければ包んだまま返す。
251
+ """
252
+ if value is None:
253
+ return None
254
+ raw = value.get_secret_value() if hasattr(value, "get_secret_value") else value
255
+ if raw is None or not str(raw).strip():
256
+ return None
257
+ return value
258
+
259
+
246
260
  @dataclass
247
261
  class S3Config:
248
262
  """AWS S3設定
249
263
 
264
+ 認証は 2 通り (3.2.3):
265
+ - ``aws_access_key_id`` と ``aws_secret_access_key`` の両方があればそれを使う (従来どおり)
266
+ - 両方とも無ければ鍵を渡さず、boto3 の既定の探し方に任せる
267
+ (EKS の IRSA・環境変数・~/.aws の設定・EKS Pod Identity・EC2 のインスタンスロールなど。どれを先に使うかは boto3 が決める)
268
+ 片方だけのときは設定の誤りとして ``S3StorageClient`` が ``StorageConfigError`` を上げる。
269
+ 空文字・空白だけの値は「無し」として扱う (設定ファイルで空にしたときの形)。
270
+
250
271
  Example:
251
- >>> # 辞書から作成
272
+ >>> # 鍵で認証 (従来)
252
273
  >>> config = S3Config.from_dict({
253
274
  ... "bucket_name": "my-bucket",
254
275
  ... "aws_access_key_id": "AKIAXXXXXXX",
@@ -256,6 +277,12 @@ class S3Config:
256
277
  ... "region_name": "ap-northeast-1"
257
278
  ... })
258
279
  >>>
280
+ >>> # 鍵なし (IRSA など boto3 の既定の探し方)
281
+ >>> config = S3Config.from_dict({
282
+ ... "bucket_name": "my-bucket",
283
+ ... "region_name": "us-east-1"
284
+ ... })
285
+ >>>
259
286
  >>> # クライアント初期化
260
287
  >>> client = S3StorageClient(config)
261
288
 
@@ -263,8 +290,8 @@ class S3Config:
263
290
  aws_access_key_id, aws_secret_access_keyはrepr=Falseでログ出力から除外されます。
264
291
  """
265
292
  bucket_name: str
266
- aws_access_key_id: str = field(repr=False) # シークレット: reprから除外
267
- aws_secret_access_key: str = field(repr=False) # シークレット: reprから除外
293
+ aws_access_key_id: Optional[str] = field(default=None, repr=False) # シークレット: reprから除外
294
+ aws_secret_access_key: Optional[str] = field(default=None, repr=False) # シークレット: reprから除外
268
295
  region_name: str = "us-east-1"
269
296
  # 共通オプション
270
297
  enabled: bool = True
@@ -277,19 +304,35 @@ class S3Config:
277
304
  # S3固有オプション
278
305
  endpoint_url: Optional[str] = None # MinIO等のS3互換ストレージ用
279
306
 
307
+ def __post_init__(self) -> None:
308
+ # 設定ファイルで `aws_access_key_id = ""` と書いた形 (SecretStr("") も) を「無し」に正規化する。
309
+ # 空でない値は変えない
310
+ self.aws_access_key_id = _blank_key_to_none(self.aws_access_key_id)
311
+ self.aws_secret_access_key = _blank_key_to_none(self.aws_secret_access_key)
312
+
280
313
  @property
281
314
  def provider(self) -> StorageProvider:
282
315
  return StorageProvider.AWS_S3
283
316
 
317
+ @property
318
+ def uses_access_keys(self) -> bool:
319
+ """鍵の 2 つで認証するか (False なら boto3 の既定の探し方)"""
320
+ return self.aws_access_key_id is not None and self.aws_secret_access_key is not None
321
+
284
322
  def __str__(self) -> str:
285
- return f"S3Config(bucket_name={self.bucket_name!r}, region_name={self.region_name!r}, credentials=***)"
323
+ credentials = "***" if self.uses_access_keys else "default"
324
+ return (
325
+ f"S3Config(bucket_name={self.bucket_name!r}, region_name={self.region_name!r}, "
326
+ f"credentials={credentials})"
327
+ )
286
328
 
287
329
  @classmethod
288
330
  def from_dict(cls, data: Dict[str, Any]) -> "S3Config":
289
331
  """辞書からS3Configを作成
290
332
 
291
333
  Args:
292
- data: 設定辞書
334
+ data: 設定辞書。``aws_access_key_id`` と ``aws_secret_access_key`` は
335
+ 両方を書くか、両方を省く (省くと boto3 の既定の探し方。IRSA など)
293
336
 
294
337
  Returns:
295
338
  S3Config instance
@@ -301,11 +344,12 @@ class S3Config:
301
344
  ... "aws_secret_access_key": "secretkey",
302
345
  ... "region_name": "ap-northeast-1"
303
346
  ... })
347
+ >>> config = S3Config.from_dict({"bucket_name": "my-bucket"})
304
348
  """
305
349
  return cls(
306
350
  bucket_name=data["bucket_name"],
307
- aws_access_key_id=data["aws_access_key_id"],
308
- aws_secret_access_key=data["aws_secret_access_key"],
351
+ aws_access_key_id=data.get("aws_access_key_id"),
352
+ aws_secret_access_key=data.get("aws_secret_access_key"),
309
353
  region_name=data.get("region_name", "us-east-1"),
310
354
  enabled=data.get("enabled", True),
311
355
  max_file_size=data.get("max_file_size", 5 * 1024 * 1024 * 1024),
@@ -65,6 +65,9 @@ class S3StorageClient(StorageClientBase):
65
65
  )
66
66
  client = S3StorageClient(config)
67
67
  result = await client.upload_file("/path/to/file.txt")
68
+
69
+ # 鍵なし: boto3 の既定の探し方 (EKS の IRSA・インスタンスロールなど)
70
+ client = S3StorageClient(S3Config(bucket_name="my-bucket", region_name="us-east-1"))
68
71
  """
69
72
 
70
73
  def __init__(self, config: S3Config):
@@ -89,20 +92,29 @@ class S3StorageClient(StorageClientBase):
89
92
  if not config.bucket_name:
90
93
  raise StorageConfigError("S3 bucket_name is required")
91
94
 
92
- if not config.aws_access_key_id or not config.aws_secret_access_key:
95
+ has_key_id = config.aws_access_key_id is not None
96
+ has_secret = config.aws_secret_access_key is not None
97
+ if has_key_id != has_secret:
98
+ # 片方だけは書き損じ。既定の探し方に黙って倒すと別の資格情報で動いてしまう
93
99
  raise StorageConfigError(
94
- "S3 requires aws_access_key_id and aws_secret_access_key"
100
+ "S3 needs both aws_access_key_id and aws_secret_access_key, or neither "
101
+ "(leave both out to use the default AWS credential chain, e.g. IRSA on EKS)"
95
102
  )
96
103
 
97
104
  self._s3_config: S3Config = config
98
105
  self._s3_client = None
99
106
 
100
107
  try:
101
- session = boto3.Session(
102
- aws_access_key_id=self._get_api_key(config.aws_access_key_id),
103
- aws_secret_access_key=self._get_api_key(config.aws_secret_access_key),
104
- region_name=config.region_name,
105
- )
108
+ if config.uses_access_keys:
109
+ session = boto3.Session(
110
+ aws_access_key_id=self._get_api_key(config.aws_access_key_id),
111
+ aws_secret_access_key=self._get_api_key(config.aws_secret_access_key),
112
+ region_name=config.region_name,
113
+ )
114
+ else:
115
+ # 鍵を渡さない: boto3 の既定の探し方に任せる(IRSA(AWS_ROLE_ARN・AWS_WEB_IDENTITY_TOKEN_FILE)・
116
+ # 環境変数・~/.aws・EKS Pod Identity・インスタンスロールなど。どれを先に使うかは boto3 が決める)
117
+ session = boto3.Session(region_name=config.region_name)
106
118
 
107
119
  client_kwargs = {}
108
120
  if config.endpoint_url:
@@ -113,7 +125,8 @@ class S3StorageClient(StorageClientBase):
113
125
 
114
126
  logger.info(
115
127
  f"S3StorageClient initialized - bucket={config.bucket_name}, "
116
- f"region={config.region_name}"
128
+ f"region={config.region_name}, "
129
+ f"credentials={'access_keys' if config.uses_access_keys else 'default_chain'}"
117
130
  )
118
131
 
119
132
  except Exception as e:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agenticstar-platform"
7
- version = "3.2.1"
7
+ version = "3.2.3"
8
8
  description = "AGENTICSTAR Platform SDK - Enterprise AI Agent Infrastructure"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -16,7 +16,7 @@ aiohttp = pytest.importorskip("aiohttp")
16
16
  from aiohttp import web # noqa: E402
17
17
 
18
18
 
19
- async def _receive_payloads(events):
19
+ async def _receive_payloads(events, make_handler=None):
20
20
  """実 HTTP サーバーを起動し、events を送信して受信ペイロード一覧を返す"""
21
21
  received = []
22
22
 
@@ -33,8 +33,9 @@ async def _receive_payloads(events):
33
33
  port = site._server.sockets[0].getsockname()[1]
34
34
 
35
35
  try:
36
- handler = WebhookEventHandler(
37
- webhook_url=f"http://127.0.0.1:{port}/webhook/notify",
36
+ url = f"http://127.0.0.1:{port}/webhook/notify"
37
+ handler = make_handler(url) if make_handler else WebhookEventHandler(
38
+ webhook_url=url,
38
39
  conversation_id="conv-1",
39
40
  message_id="msg-1",
40
41
  request_source="external",
@@ -84,6 +85,62 @@ async def test_webhook_top_level_type_routing():
84
85
  assert all(p["data"]["request_source"] == "external" for p in payloads)
85
86
 
86
87
 
88
+ class _AcceptingDataAccess:
89
+ """DatabaseEventHandler の保存先の代わり(insert を記録して成功を返す)"""
90
+
91
+ def __init__(self):
92
+ self.rows = []
93
+
94
+ async def insert(self, table, row):
95
+ self.rows.append((table, row))
96
+ return {"success": True}
97
+
98
+
99
+ async def test_marketplace_handler_sends_failure_as_stop_like_the_worker():
100
+ """create_marketplace_handler は失敗の完了も finish_reason "stop"(3.2.3)。
101
+
102
+ 受信側(chatboardfront monitoring.js)は "stop" のときだけメッセージを終わらせ、"error" は読まない。
103
+ 本体 PodWebhookSubscriber も COMPLETION_FAILURE を "stop" で送る。途中のイベントには付けない。
104
+ run_marketplace_agent もこの handler を使う。
105
+ """
106
+ from agenticstar_platform.events.handlers import create_marketplace_handler
107
+
108
+ data_access = _AcceptingDataAccess()
109
+ payloads = await _receive_payloads(
110
+ [
111
+ StreamingEvent(EventType.PHASE_START, "e1", "処理開始"),
112
+ StreamingEvent(EventType.COMPLETION_FAILURE, "e1", "agent failed: ValueError: boom"),
113
+ ],
114
+ make_handler=lambda url: create_marketplace_handler(
115
+ data_access=data_access, webhook_url=url, user_id="u1",
116
+ conversation_id="conv-1", message_id="msg-1", request_source="internal",
117
+ token_provider=lambda: None,
118
+ ),
119
+ )
120
+
121
+ assert payloads[0]["data"]["finish_reason"] is None
122
+ assert payloads[1]["type"] == "append_message"
123
+ assert payloads[1]["data"]["chunk_type"] == "content"
124
+ assert payloads[1]["data"]["content"] == {"content": "agent failed: ValueError: boom"}
125
+ assert payloads[1]["data"]["finish_reason"] == "stop"
126
+ assert len(data_access.rows) == 2 # DB への保存は今どおり
127
+
128
+
129
+ async def test_webhook_handler_keeps_error_for_failure_by_default():
130
+ """WebhookEventHandler を単体で使う受け口は今どおり "error"(3.2.3 の後も変わらない)。引数で "stop" にできる"""
131
+ failure = StreamingEvent(EventType.COMPLETION_FAILURE, "e1", "boom")
132
+ default = await _receive_payloads([failure])
133
+ assert default[0]["data"]["finish_reason"] == "error"
134
+
135
+ stop = await _receive_payloads(
136
+ [failure],
137
+ make_handler=lambda url: WebhookEventHandler(
138
+ webhook_url=url, conversation_id="conv-1", message_id="msg-1", failure_finish_reason="stop",
139
+ ),
140
+ )
141
+ assert stop[0]["data"]["finish_reason"] == "stop"
142
+
143
+
87
144
  def test_build_content_data_shapes():
88
145
  vnc = StreamingEvent(
89
146
  EventType.HITL_REQUIRED_BROWSER_VNC, "e1", "VNC", metadata={"vnc_url": "u"},