agenticstar-platform 3.2.0__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 (148) hide show
  1. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/PKG-INFO +165 -6
  2. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/README.md +164 -5
  3. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/__init__.py +1 -1
  4. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/events/handlers.py +37 -4
  5. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/runner.py +3 -1
  6. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/storage/base.py +51 -7
  7. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/storage/s3.py +21 -8
  8. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/child.py +10 -4
  9. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/context.py +46 -2
  10. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/envelope.py +60 -6
  11. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/runner.py +39 -3
  12. agenticstar_platform-3.2.3/agenticstar_platform/workflow/testing.py +449 -0
  13. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/docs/examples/workflow_reply_agent.py +42 -0
  14. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/pyproject.toml +1 -1
  15. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/events/test_handler_type_routing.py +60 -3
  16. agenticstar_platform-3.2.3/tests/events/test_notify_token.py +101 -0
  17. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/runner/test_marketplace_runner_started_fence.py +21 -0
  18. agenticstar_platform-3.2.3/tests/storage/test_s3_credential.py +178 -0
  19. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/workflow/agent_fixtures.py +87 -0
  20. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/workflow/test_child_failure_summary.py +9 -0
  21. agenticstar_platform-3.2.3/tests/workflow/test_workflow_incomplete.py +322 -0
  22. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/workflow/test_workflow_runner.py +14 -8
  23. agenticstar_platform-3.2.3/tests/workflow/test_workflow_testing.py +229 -0
  24. agenticstar_platform-3.2.0/.gitignore +0 -270
  25. agenticstar_platform-3.2.0/tests/workflow/fake_runtime.py +0 -267
  26. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/LICENSE +0 -0
  27. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/_config_utils.py +0 -0
  28. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/audit/ROW_HASH_SPEC.md +0 -0
  29. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/audit/__init__.py +0 -0
  30. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/audit/action_audit.py +0 -0
  31. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/audit/guardrail_alerts.py +0 -0
  32. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/audit/row_hash_fixture.json +0 -0
  33. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/auth/__init__.py +0 -0
  34. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/auth/client.py +0 -0
  35. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/auth/config.py +0 -0
  36. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/auth/exceptions.py +0 -0
  37. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/auth/models.py +0 -0
  38. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/common/__init__.py +0 -0
  39. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/common/context_manager.py +0 -0
  40. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/common/secrets.py +0 -0
  41. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/common/validation.py +0 -0
  42. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/__init__.py +0 -0
  43. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/api_manager.py +0 -0
  44. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/completion_report.py +0 -0
  45. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/config.py +0 -0
  46. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/config_access.py +0 -0
  47. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/data_access.py +0 -0
  48. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/execution_access.py +0 -0
  49. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/factory.py +0 -0
  50. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/manager.py +0 -0
  51. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/pod_runtime.py +0 -0
  52. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/started_fence.py +0 -0
  53. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/telemetry_access.py +0 -0
  54. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/db/wire_types.py +0 -0
  55. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/events/__init__.py +0 -0
  56. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/events/emitter.py +0 -0
  57. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/events/models.py +0 -0
  58. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/events/types.py +0 -0
  59. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/README.md +0 -0
  60. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/__init__.py +0 -0
  61. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/__main__.py +0 -0
  62. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/checks.py +0 -0
  63. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/docker-compose.yml +0 -0
  64. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/doctor.py +0 -0
  65. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/lab_config.py +0 -0
  66. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/offline_embedding.py +0 -0
  67. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/run_lab.py +0 -0
  68. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/lab/seed.py +0 -0
  69. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/memory/__init__.py +0 -0
  70. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/memory/semantic.py +0 -0
  71. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/metering/__init__.py +0 -0
  72. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/metering/meter.py +0 -0
  73. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/policy/__init__.py +0 -0
  74. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/policy/internal_operations.py +0 -0
  75. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/policy/model.py +0 -0
  76. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/rag/__init__.py +0 -0
  77. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/rag/base.py +0 -0
  78. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/rag/embedding.py +0 -0
  79. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/rag/qdrant_manager.py +0 -0
  80. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/runtime.py +0 -0
  81. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/security/__init__.py +0 -0
  82. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/security/aws.py +0 -0
  83. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/security/azure.py +0 -0
  84. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/security/base.py +0 -0
  85. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/security/content_safety_validator.py +0 -0
  86. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/security/factory.py +0 -0
  87. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/security/gcp.py +0 -0
  88. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/storage/__init__.py +0 -0
  89. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/storage/azure.py +0 -0
  90. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/storage/gcs.py +0 -0
  91. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/storage/paths.py +0 -0
  92. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/__init__.py +0 -0
  93. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/canonical.py +0 -0
  94. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/errors.py +0 -0
  95. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/files.py +0 -0
  96. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/runtime_client.py +0 -0
  97. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/agenticstar_platform/workflow/schema.py +0 -0
  98. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/docs/agenticstar_platform.html +0 -0
  99. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/docs/examples/workflow_mock_send_agent.py +0 -0
  100. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/docs/index.html +0 -0
  101. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/docs/search.js +0 -0
  102. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/__init__.py +0 -0
  103. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/audit/__init__.py +0 -0
  104. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/audit/test_action_audit_30.py +0 -0
  105. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/audit/test_row_hash_fixture.py +0 -0
  106. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/auth/__init__.py +0 -0
  107. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/auth/test_auth_type_bearer_only.py +0 -0
  108. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/auth/test_get_user_path_encoding.py +0 -0
  109. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/auth/test_mcp_tokens_expiry.py +0 -0
  110. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/auth/test_mcp_tokens_rejected.py +0 -0
  111. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/db/__init__.py +0 -0
  112. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/db/test_api_manager_drain_and_headers.py +0 -0
  113. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/db/test_completion_report.py +0 -0
  114. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/db/test_data_access_identifier_validation.py +0 -0
  115. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/db/test_manager_execute_query_dispatch.py +0 -0
  116. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/db/test_telemetry_tools_used_contract.py +0 -0
  117. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/db/test_wire_param_types.py +0 -0
  118. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/events/__init__.py +0 -0
  119. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/lab/test_lab_contract.py +0 -0
  120. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/lab/test_lab_docker.py +0 -0
  121. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/memory/__init__.py +0 -0
  122. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/memory/test_semantic_converter_base_url.py +0 -0
  123. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/metering/__init__.py +0 -0
  124. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/metering/test_meter_cache_split.py +0 -0
  125. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/policy/__init__.py +0 -0
  126. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/policy/test_a2a_connection_rule.py +0 -0
  127. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/policy/test_decision_layer.py +0 -0
  128. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/policy/test_evaluate_decision_table.py +0 -0
  129. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/policy/test_policy_model.py +0 -0
  130. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/policy/test_scoped_forbid.py +0 -0
  131. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/rag/__init__.py +0 -0
  132. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/rag/test_embedding_dimensions.py +0 -0
  133. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/rag/test_qdrant_error_hierarchy.py +0 -0
  134. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/rag/test_qdrant_get_statistics.py +0 -0
  135. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/runner/__init__.py +0 -0
  136. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/runner/test_marketplace_runner_contract.py +0 -0
  137. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/runner/test_marketplace_runner_db_fallback.py +0 -0
  138. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/security/__init__.py +0 -0
  139. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/security/test_azure_connect_retry.py +0 -0
  140. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/security/test_azure_prompt_shield_documents.py +0 -0
  141. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/security/test_factory_vertex_ai_safety.py +0 -0
  142. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/security/test_gcp_vertex_ai_safety.py +0 -0
  143. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/storage/__init__.py +0 -0
  144. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/storage/test_azure_credential.py +0 -0
  145. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/test_lazy_import_contract.py +0 -0
  146. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/test_readme_quickstart.py +0 -0
  147. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/test_storage_provider_extra_contract.py +0 -0
  148. {agenticstar_platform-3.2.0 → agenticstar_platform-3.2.3}/tests/workflow/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agenticstar-platform
3
- Version: 3.2.0
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.
@@ -687,7 +707,9 @@ What `context` provides: correlation (`execution_id` / `run_id` / `operation_key
687
707
  do not start an external write without that acknowledgement), `is_cancel_requested()` /
688
708
  `ensure_can_act()` / `deadline_remaining()`, `skip(reason)` (a step with nothing to do: `no_target` /
689
709
  `not_applicable` / `nothing_to_do` → `skipped`), `policy_unavailable()` (the policy could not be
690
- checked before any external action → `skipped/policy_unavailable`), `log(message)` (written to the runner's
710
+ checked before any external action → `skipped/policy_unavailable`), `incomplete(reason, comment=None)` (SDK ≥ 3.2.1:
711
+ the step could not be completed and running it again would not help → `incomplete/<reason>`; see below),
712
+ `log(message)` (written to the runner's
691
713
  log; `run()` sends SDK logs at INFO to stderr when the process has not configured logging),
692
714
  `gate_decision` (SDK ≥ 3.0.9: the review decision that started this run — `None` unless the run follows a
693
715
  review; otherwise a read-only mapping `{gate_id, decision, decided_by, note, decided_at}` where `decision` is
@@ -730,11 +752,47 @@ async def run(context):
730
752
  ...
731
753
  ```
732
754
 
755
+ **Ending a step as not completed** (SDK ≥ 3.2.1). When your agent cannot complete the step and running it again would
756
+ give the same result — the instructions are missing, a setting is wrong — call `context.incomplete(reason, comment)` and
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.
760
+
761
+ ```python
762
+ async def run(context):
763
+ instructions = (context.work_dir / "instructions.md").read_text(encoding="utf-8")
764
+ if not instructions.strip():
765
+ await context.incomplete("missing_instructions",
766
+ comment="instructions.md is empty. Write the instructions and run the step again.")
767
+ return
768
+ ...
769
+ ```
770
+
771
+ - `reason` is a reason code with the same rules as `report_completion()` of cycle executions: lowercase snake_case of at
772
+ most 32 characters (`-` and spaces become `_`, upper case becomes lower case). Any other value (a sentence, an exception
773
+ string) becomes `incomplete` and a warning is logged
774
+ - `comment` (optional) is a short explanation for the person who checks the step. It is cut at 2,048 characters (counted
775
+ as UTF-16 code units, like JavaScript). Do not put secrets or personal data in it
776
+ - Declare once. A second call is not recorded (the first one is kept and the runner logs a warning). An exception after
777
+ the declaration still ends the step as `incomplete`
778
+ - An audit persistence failure, cancellation, the deadline, the execution being closed elsewhere, a pod stop signal and
779
+ a denied tool call decide the outcome before the declaration
780
+ - For a failure that may go away on its own (a timeout of an external service, a rate limit), raise an exception
781
+ instead: the platform retries `failed/agent_error` (up to its retry limit) when the contract's effect mode is
782
+ `read_only` or `idempotent_write` and the effects are known
783
+
784
+ The completion then carries an **end-of-run report**, `completion_report`, in the same form as the platform's own agents:
785
+ `{"completed": false, "reason_code": ..., "comment": ...}`. For `incomplete` the comment is yours (no `comment` key when
786
+ you gave none); for an exception (`failed/agent_error`) the comment is the cause chain the runner writes to stderr —
787
+ class names, the SDK's error code, the HTTP status and the destination host name only, never messages, URLs or tokens.
788
+ Other outcomes carry no report. A platform that does not read the report yet keeps the completion and drops the report.
789
+
733
790
  The outcome describes how the run ended, with 7 values (`completed` / `incomplete` / `failed` /
734
791
  `timeout` / `stopped` / `denied` / `skipped`). **Success means the agent ran to completion**: the agent
735
792
  function returns normally → `completed/completed`, unless an audit persistence failure, a denied tool
736
- call, an invalid declared output, a skip or a missing required output decides otherwise (failed writes,
737
- unobserved effects or zero observations do not lower the outcome); an exception → `failed/agent_error`; cancellation →
793
+ call, a declared `incomplete`, an invalid declared output, a skip or a missing required output decides otherwise (failed writes,
794
+ unobserved effects or zero observations do not lower the outcome); `context.incomplete(reason)` → `incomplete/<reason>`;
795
+ an exception → `failed/agent_error`; cancellation →
738
796
  `stopped/cancelled`; deadline → `timeout/execution_timeout`; pod stop signal → `failed/runner_stopped`;
739
797
  a call reported with `audit.tool_denied` → `denied`.
740
798
  What was written is kept in `counts` / `tools` and `had_mutating_success` (true = success observed /
@@ -743,6 +801,83 @@ automatically and go to a human (effects that already happened are never undone)
743
801
  source distribution: `docs/examples/workflow_reply_agent.py` (drafting a reply) /
744
802
  `workflow_mock_send_agent.py` (a mock send).
745
803
 
804
+ **Testing an agent without ASTER** (SDK ≥ 3.2.1). `agenticstar_platform.workflow.testing.FakeRuntime` answers the
805
+ platform's runtime API in memory. Give it your contract, the step's parameters, the input files, the connection tokens,
806
+ the effect mode and the deadline, run one step with `arun(...)`, then read what the runner saved. With this layout:
807
+
808
+ ```text
809
+ my_project/
810
+ my_agent/__init__.py (empty)
811
+ my_agent/main.py (your agent)
812
+ try_it.py (the script below)
813
+ ```
814
+
815
+ ```python
816
+ # my_agent/main.py
817
+ async def run(context):
818
+ instructions = (context.work_dir / "instructions.md").read_text(encoding="utf-8")
819
+ (context.output_dir / "reply.md").write_text(f"# Reply\n\n{instructions}", encoding="utf-8")
820
+ ```
821
+
822
+ ```python
823
+ # try_it.py
824
+ import asyncio
825
+ import tempfile
826
+
827
+ from agenticstar_platform.workflow import arun
828
+ from agenticstar_platform.workflow.testing import FakeRuntime
829
+
830
+ CONTRACT = {
831
+ "runtime_protocol": "mp-workflow/1", "effect_mode": "read_only",
832
+ "files": [{"name": "instructions", "path": "instructions.md", "format": "markdown", "required": True}],
833
+ "outputs": [{"name": "reply", "path": "reply.md", "format": "markdown", "required": True}],
834
+ "limits": {"execution_timeout_seconds": 600},
835
+ }
836
+
837
+
838
+ async def main():
839
+ fake = FakeRuntime(
840
+ contract=CONTRACT,
841
+ parameters={"product_code": "P-100", "language": "en"},
842
+ inputs={"instructions": b"Thank the customer.\n"}, # by the name of a contract file
843
+ connections={"crm": "test-access-token"}, # slot -> token, or (token, expires_at)
844
+ deadline_seconds=600,
845
+ )
846
+ with tempfile.TemporaryDirectory() as workspace:
847
+ result = await arun("my_agent.main:run", env=fake.runner_env(), workspace=workspace, transport=fake.transport)
848
+ print(result.outcome, result.outcome_reason_code) # completed completed
849
+ print(fake.saved_artifacts()["reply"]["content"]) # the saved output, by its name
850
+ print([e["event_type"] for e in fake.saved_audit_events()]) # context.manifest x2, then your audit events
851
+ print(fake.saved_completion()["envelope"]) # the completion_envelope
852
+
853
+
854
+ asyncio.run(main())
855
+ ```
856
+
857
+ ```bash
858
+ cd my_project
859
+ PYTHONPATH="$PWD" python try_it.py
860
+ ```
861
+
862
+ - The runner starts your entrypoint in a child process whose current directory is the work directory, so `my_agent.main`
863
+ must be importable from `PYTHONPATH` **as an absolute path** (`PYTHONPATH="$PWD"`, not `PYTHONPATH=.`: the child would
864
+ look for `.` in the work directory) or installed. In a test, set `os.environ["PYTHONPATH"]` to the absolute path before
865
+ calling `arun`
866
+ - The fake copies the current behaviour of the runtime API (only declared outputs are saved, a `completed` outcome needs
867
+ the required outputs, writes after cancellation, the end of the run or the deadline are refused except the audit
868
+ records of what the agent already did (`tool.effect` / `tool.denied`), ...). It **does not check the parameters
869
+ against `parameters_schema` or validate the contract**: the platform checks those before a run (the admin console's
870
+ check before registration and the executor). Parameters are passed to your agent as given
871
+ - **Supported surface**: the constructor arguments, `runner_env()`, `transport`, `saved_artifacts()` /
872
+ `saved_audit_events()` / `saved_completion()`, `sample_contract()` / `sample_parameters()`, the constants
873
+ `EXECUTION_ID` / `TOKEN` / `BASE_URL`, and the attributes that simulate the platform, which you can change before or
874
+ during a run: `contract` (a copy of the one you passed; later requests see your changes), `cancel_requested_at` (an ISO time = cancellation
875
+ was requested), `run_closed`, `deadline_passed`, `gate_decision`, `start_input`, `fail_audit_types`,
876
+ `fail_upload_times`, `fail_completion_times`, `fail_download_times` (see the module's docstring). Everything else is
877
+ internal and may change
878
+ - Without arguments the fake uses the contract and parameters of `docs/examples/workflow_reply_agent.py`; run
879
+ `python docs/examples/workflow_reply_agent.py` (source distribution) to see one step end to end
880
+
746
881
  ### Policy Module — `agenticstar_platform.policy` (SDK ≥ 1.0.0a1)
747
882
 
748
883
  Pure computation (no I/O, no agent framework): decide **allow / forbid** for a tool call from a two-layer policy.
@@ -993,6 +1128,30 @@ See the [developer portal](https://developers.fd.agenticstar.tm.softbank.jp/) fo
993
1128
 
994
1129
  ## Changelog
995
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
+
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`)
1146
+
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.
1148
+ - **The completion carries an end-of-run report** (`completion_report`: `completed: false`, `reason_code`, `comment`) for `incomplete` (the agent's comment) and for an agent exception, `failed/agent_error` (the redacted cause chain the child already writes to stderr since 3.1.3: class names, the SDK's error code, the HTTP status and the host name; at most 2,048 UTF-16 code units, also with `ASTER_WORKFLOW_DEBUG_TRACEBACK=1`). Other outcomes carry no report, so their completion is the same as in 3.2. A platform that does not read the report yet keeps the completion and drops the report. A `comment` that is not a string (an exception, for example) is left out with a warning, so its message never reaches the report.
1149
+ - **The cause chain shows an `error_code` only in the SDK's own form** (upper-case snake_case of at most 64 characters, such as `REQUEST_ERROR`), in stderr and in the report. An SDK exception that agent code created with another value no longer shows that value.
1150
+ - **New `agenticstar_platform.workflow.testing`**: `FakeRuntime`, an in-memory copy of the platform's runtime API for testing an agent without ASTER. It takes the contract, parameters, input files, connection tokens, effect mode, deadline and instructions as arguments, and `saved_artifacts()` / `saved_audit_events()` / `saved_completion()` read what the runner saved. It does not check the parameters or the contract (the platform checks them before a run). Like the platform, it accepts the audit records of what the agent already did (`tool.effect` / `tool.denied`) after cancellation, the end of the run or the deadline, and reports the end of the run in the context's `cancel_requested_at`. The supported surface (constructor arguments, `runner_env()`, `transport`, the `saved_*()` readers and the listed simulation attributes) is described in the module's docstring; everything else is internal. The SDK's own tests now use it. `import agenticstar_platform.workflow` does not import it.
1151
+ - The sample `docs/examples/workflow_reply_agent.py` ends as `incomplete/missing_instructions` when the instructions are empty, and running the file runs one step against `FakeRuntime`.
1152
+ - `build_envelope(...)` accepts an optional `completion_report`. Other public functions of the runner and the context are unchanged.
1153
+ - To use it, rebuild the agent image with this SDK.
1154
+
996
1155
  ### 3.2.0 (2026-10-26) — auth: the `api_key` authentication mode (`X-Internal-API-Key` header) is removed
997
1156
 
998
1157
  The version follows the platform release (3.2.0).
@@ -1429,4 +1588,4 @@ Marketplace SDK reliability fixes (surfaced while building agents from the guide
1429
1588
 
1430
1589
  ## Version
1431
1590
 
1432
- 3.2.0
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.
@@ -606,7 +626,9 @@ What `context` provides: correlation (`execution_id` / `run_id` / `operation_key
606
626
  do not start an external write without that acknowledgement), `is_cancel_requested()` /
607
627
  `ensure_can_act()` / `deadline_remaining()`, `skip(reason)` (a step with nothing to do: `no_target` /
608
628
  `not_applicable` / `nothing_to_do` → `skipped`), `policy_unavailable()` (the policy could not be
609
- checked before any external action → `skipped/policy_unavailable`), `log(message)` (written to the runner's
629
+ checked before any external action → `skipped/policy_unavailable`), `incomplete(reason, comment=None)` (SDK ≥ 3.2.1:
630
+ the step could not be completed and running it again would not help → `incomplete/<reason>`; see below),
631
+ `log(message)` (written to the runner's
610
632
  log; `run()` sends SDK logs at INFO to stderr when the process has not configured logging),
611
633
  `gate_decision` (SDK ≥ 3.0.9: the review decision that started this run — `None` unless the run follows a
612
634
  review; otherwise a read-only mapping `{gate_id, decision, decided_by, note, decided_at}` where `decision` is
@@ -649,11 +671,47 @@ async def run(context):
649
671
  ...
650
672
  ```
651
673
 
674
+ **Ending a step as not completed** (SDK ≥ 3.2.1). When your agent cannot complete the step and running it again would
675
+ give the same result — the instructions are missing, a setting is wrong — call `context.incomplete(reason, comment)` and
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.
679
+
680
+ ```python
681
+ async def run(context):
682
+ instructions = (context.work_dir / "instructions.md").read_text(encoding="utf-8")
683
+ if not instructions.strip():
684
+ await context.incomplete("missing_instructions",
685
+ comment="instructions.md is empty. Write the instructions and run the step again.")
686
+ return
687
+ ...
688
+ ```
689
+
690
+ - `reason` is a reason code with the same rules as `report_completion()` of cycle executions: lowercase snake_case of at
691
+ most 32 characters (`-` and spaces become `_`, upper case becomes lower case). Any other value (a sentence, an exception
692
+ string) becomes `incomplete` and a warning is logged
693
+ - `comment` (optional) is a short explanation for the person who checks the step. It is cut at 2,048 characters (counted
694
+ as UTF-16 code units, like JavaScript). Do not put secrets or personal data in it
695
+ - Declare once. A second call is not recorded (the first one is kept and the runner logs a warning). An exception after
696
+ the declaration still ends the step as `incomplete`
697
+ - An audit persistence failure, cancellation, the deadline, the execution being closed elsewhere, a pod stop signal and
698
+ a denied tool call decide the outcome before the declaration
699
+ - For a failure that may go away on its own (a timeout of an external service, a rate limit), raise an exception
700
+ instead: the platform retries `failed/agent_error` (up to its retry limit) when the contract's effect mode is
701
+ `read_only` or `idempotent_write` and the effects are known
702
+
703
+ The completion then carries an **end-of-run report**, `completion_report`, in the same form as the platform's own agents:
704
+ `{"completed": false, "reason_code": ..., "comment": ...}`. For `incomplete` the comment is yours (no `comment` key when
705
+ you gave none); for an exception (`failed/agent_error`) the comment is the cause chain the runner writes to stderr —
706
+ class names, the SDK's error code, the HTTP status and the destination host name only, never messages, URLs or tokens.
707
+ Other outcomes carry no report. A platform that does not read the report yet keeps the completion and drops the report.
708
+
652
709
  The outcome describes how the run ended, with 7 values (`completed` / `incomplete` / `failed` /
653
710
  `timeout` / `stopped` / `denied` / `skipped`). **Success means the agent ran to completion**: the agent
654
711
  function returns normally → `completed/completed`, unless an audit persistence failure, a denied tool
655
- call, an invalid declared output, a skip or a missing required output decides otherwise (failed writes,
656
- unobserved effects or zero observations do not lower the outcome); an exception → `failed/agent_error`; cancellation →
712
+ call, a declared `incomplete`, an invalid declared output, a skip or a missing required output decides otherwise (failed writes,
713
+ unobserved effects or zero observations do not lower the outcome); `context.incomplete(reason)` → `incomplete/<reason>`;
714
+ an exception → `failed/agent_error`; cancellation →
657
715
  `stopped/cancelled`; deadline → `timeout/execution_timeout`; pod stop signal → `failed/runner_stopped`;
658
716
  a call reported with `audit.tool_denied` → `denied`.
659
717
  What was written is kept in `counts` / `tools` and `had_mutating_success` (true = success observed /
@@ -662,6 +720,83 @@ automatically and go to a human (effects that already happened are never undone)
662
720
  source distribution: `docs/examples/workflow_reply_agent.py` (drafting a reply) /
663
721
  `workflow_mock_send_agent.py` (a mock send).
664
722
 
723
+ **Testing an agent without ASTER** (SDK ≥ 3.2.1). `agenticstar_platform.workflow.testing.FakeRuntime` answers the
724
+ platform's runtime API in memory. Give it your contract, the step's parameters, the input files, the connection tokens,
725
+ the effect mode and the deadline, run one step with `arun(...)`, then read what the runner saved. With this layout:
726
+
727
+ ```text
728
+ my_project/
729
+ my_agent/__init__.py (empty)
730
+ my_agent/main.py (your agent)
731
+ try_it.py (the script below)
732
+ ```
733
+
734
+ ```python
735
+ # my_agent/main.py
736
+ async def run(context):
737
+ instructions = (context.work_dir / "instructions.md").read_text(encoding="utf-8")
738
+ (context.output_dir / "reply.md").write_text(f"# Reply\n\n{instructions}", encoding="utf-8")
739
+ ```
740
+
741
+ ```python
742
+ # try_it.py
743
+ import asyncio
744
+ import tempfile
745
+
746
+ from agenticstar_platform.workflow import arun
747
+ from agenticstar_platform.workflow.testing import FakeRuntime
748
+
749
+ CONTRACT = {
750
+ "runtime_protocol": "mp-workflow/1", "effect_mode": "read_only",
751
+ "files": [{"name": "instructions", "path": "instructions.md", "format": "markdown", "required": True}],
752
+ "outputs": [{"name": "reply", "path": "reply.md", "format": "markdown", "required": True}],
753
+ "limits": {"execution_timeout_seconds": 600},
754
+ }
755
+
756
+
757
+ async def main():
758
+ fake = FakeRuntime(
759
+ contract=CONTRACT,
760
+ parameters={"product_code": "P-100", "language": "en"},
761
+ inputs={"instructions": b"Thank the customer.\n"}, # by the name of a contract file
762
+ connections={"crm": "test-access-token"}, # slot -> token, or (token, expires_at)
763
+ deadline_seconds=600,
764
+ )
765
+ with tempfile.TemporaryDirectory() as workspace:
766
+ result = await arun("my_agent.main:run", env=fake.runner_env(), workspace=workspace, transport=fake.transport)
767
+ print(result.outcome, result.outcome_reason_code) # completed completed
768
+ print(fake.saved_artifacts()["reply"]["content"]) # the saved output, by its name
769
+ print([e["event_type"] for e in fake.saved_audit_events()]) # context.manifest x2, then your audit events
770
+ print(fake.saved_completion()["envelope"]) # the completion_envelope
771
+
772
+
773
+ asyncio.run(main())
774
+ ```
775
+
776
+ ```bash
777
+ cd my_project
778
+ PYTHONPATH="$PWD" python try_it.py
779
+ ```
780
+
781
+ - The runner starts your entrypoint in a child process whose current directory is the work directory, so `my_agent.main`
782
+ must be importable from `PYTHONPATH` **as an absolute path** (`PYTHONPATH="$PWD"`, not `PYTHONPATH=.`: the child would
783
+ look for `.` in the work directory) or installed. In a test, set `os.environ["PYTHONPATH"]` to the absolute path before
784
+ calling `arun`
785
+ - The fake copies the current behaviour of the runtime API (only declared outputs are saved, a `completed` outcome needs
786
+ the required outputs, writes after cancellation, the end of the run or the deadline are refused except the audit
787
+ records of what the agent already did (`tool.effect` / `tool.denied`), ...). It **does not check the parameters
788
+ against `parameters_schema` or validate the contract**: the platform checks those before a run (the admin console's
789
+ check before registration and the executor). Parameters are passed to your agent as given
790
+ - **Supported surface**: the constructor arguments, `runner_env()`, `transport`, `saved_artifacts()` /
791
+ `saved_audit_events()` / `saved_completion()`, `sample_contract()` / `sample_parameters()`, the constants
792
+ `EXECUTION_ID` / `TOKEN` / `BASE_URL`, and the attributes that simulate the platform, which you can change before or
793
+ during a run: `contract` (a copy of the one you passed; later requests see your changes), `cancel_requested_at` (an ISO time = cancellation
794
+ was requested), `run_closed`, `deadline_passed`, `gate_decision`, `start_input`, `fail_audit_types`,
795
+ `fail_upload_times`, `fail_completion_times`, `fail_download_times` (see the module's docstring). Everything else is
796
+ internal and may change
797
+ - Without arguments the fake uses the contract and parameters of `docs/examples/workflow_reply_agent.py`; run
798
+ `python docs/examples/workflow_reply_agent.py` (source distribution) to see one step end to end
799
+
665
800
  ### Policy Module — `agenticstar_platform.policy` (SDK ≥ 1.0.0a1)
666
801
 
667
802
  Pure computation (no I/O, no agent framework): decide **allow / forbid** for a tool call from a two-layer policy.
@@ -912,6 +1047,30 @@ See the [developer portal](https://developers.fd.agenticstar.tm.softbank.jp/) fo
912
1047
 
913
1048
  ## Changelog
914
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
+
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`)
1065
+
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.
1067
+ - **The completion carries an end-of-run report** (`completion_report`: `completed: false`, `reason_code`, `comment`) for `incomplete` (the agent's comment) and for an agent exception, `failed/agent_error` (the redacted cause chain the child already writes to stderr since 3.1.3: class names, the SDK's error code, the HTTP status and the host name; at most 2,048 UTF-16 code units, also with `ASTER_WORKFLOW_DEBUG_TRACEBACK=1`). Other outcomes carry no report, so their completion is the same as in 3.2. A platform that does not read the report yet keeps the completion and drops the report. A `comment` that is not a string (an exception, for example) is left out with a warning, so its message never reaches the report.
1068
+ - **The cause chain shows an `error_code` only in the SDK's own form** (upper-case snake_case of at most 64 characters, such as `REQUEST_ERROR`), in stderr and in the report. An SDK exception that agent code created with another value no longer shows that value.
1069
+ - **New `agenticstar_platform.workflow.testing`**: `FakeRuntime`, an in-memory copy of the platform's runtime API for testing an agent without ASTER. It takes the contract, parameters, input files, connection tokens, effect mode, deadline and instructions as arguments, and `saved_artifacts()` / `saved_audit_events()` / `saved_completion()` read what the runner saved. It does not check the parameters or the contract (the platform checks them before a run). Like the platform, it accepts the audit records of what the agent already did (`tool.effect` / `tool.denied`) after cancellation, the end of the run or the deadline, and reports the end of the run in the context's `cancel_requested_at`. The supported surface (constructor arguments, `runner_env()`, `transport`, the `saved_*()` readers and the listed simulation attributes) is described in the module's docstring; everything else is internal. The SDK's own tests now use it. `import agenticstar_platform.workflow` does not import it.
1070
+ - The sample `docs/examples/workflow_reply_agent.py` ends as `incomplete/missing_instructions` when the instructions are empty, and running the file runs one step against `FakeRuntime`.
1071
+ - `build_envelope(...)` accepts an optional `completion_report`. Other public functions of the runner and the context are unchanged.
1072
+ - To use it, rebuild the agent image with this SDK.
1073
+
915
1074
  ### 3.2.0 (2026-10-26) — auth: the `api_key` authentication mode (`X-Internal-API-Key` header) is removed
916
1075
 
917
1076
  The version follows the platform release (3.2.0).
@@ -1348,4 +1507,4 @@ Marketplace SDK reliability fixes (surfaced while building agents from the guide
1348
1507
 
1349
1508
  ## Version
1350
1509
 
1351
- 3.2.0
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.0"
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"]