oneshot-python 0.14.0__tar.gz → 0.16.0__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 (20) hide show
  1. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/PKG-INFO +1 -1
  2. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/oneshot/client.py +49 -15
  3. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/pyproject.toml +1 -1
  4. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_email_payload.py +12 -0
  5. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/.gitignore +0 -0
  6. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/README.md +0 -0
  7. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/oneshot/__init__.py +0 -0
  8. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/oneshot/_errors.py +0 -0
  9. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/oneshot/_types.py +0 -0
  10. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/oneshot/x402.py +0 -0
  11. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/__init__.py +0 -0
  12. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_balance.py +0 -0
  13. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_compute.py +0 -0
  14. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_emergency_error.py +0 -0
  15. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_max_cost_header.py +0 -0
  16. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_phones_pending.py +0 -0
  17. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_request_id.py +0 -0
  18. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_tag_receipt_value.py +0 -0
  19. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_x402.py +0 -0
  20. {oneshot_python-0.14.0 → oneshot_python-0.16.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oneshot-python
3
- Version: 0.14.0
3
+ Version: 0.16.0
4
4
  Summary: Core Python SDK for the OneShot API — HTTP client with x402 payment handling
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.10
@@ -38,7 +38,7 @@ try:
38
38
 
39
39
  SDK_VERSION = _pkg_version("oneshot-python")
40
40
  except Exception: # pragma: no cover - editable/source runs without dist metadata
41
- SDK_VERSION = "0.12.1"
41
+ SDK_VERSION = "0.16.0"
42
42
 
43
43
  # ---------------------------------------------------------------------------
44
44
  # Environment configuration
@@ -62,6 +62,7 @@ def _build_email_payload(
62
62
  extra: dict[str, Any],
63
63
  *,
64
64
  reply_to_email_id: Optional[str] = None,
65
+ mailbox_mode: Optional[str] = None,
65
66
  ) -> dict[str, Any]:
66
67
  """Build the /v1/tools/email/send body in the API's field convention.
67
68
 
@@ -93,6 +94,8 @@ def _build_email_payload(
93
94
  payload["reply_to_email_id"] = reply_to_email_id
94
95
  if from_name:
95
96
  payload["from_name"] = from_name
97
+ if mailbox_mode:
98
+ payload["mailbox_mode"] = mailbox_mode
96
99
  return payload
97
100
 
98
101
 
@@ -760,6 +763,7 @@ class OneShotClient:
760
763
  from_mailbox: Optional[str] = None,
761
764
  from_name: Optional[str] = None,
762
765
  reply_to_email_id: Optional[str] = None,
766
+ mailbox_mode: Optional[str] = None,
763
767
  **kwargs: Any,
764
768
  ) -> Any:
765
769
  """Send an email. Blocking.
@@ -767,10 +771,21 @@ class OneShotClient:
767
771
  Set ``reply_to_email_id`` (an inbound email id from ``inbox()``) to
768
772
  reply within a thread — ``to``/``subject`` are then derived from the
769
773
  inbound message unless given.
774
+
775
+ The first send from any ``from_mailbox`` on an owned domain — including
776
+ the default ``agent@`` — provisions a mailbox per address and adds a
777
+ one-time ``mailbox_provisioning_fee`` to the quote; it's free only once
778
+ that address is already provisioned (``active``).
779
+
780
+ ``mailbox_mode`` ('relay' | 'mailbox', default 'relay') selects how a
781
+ domain sends: 'relay' is header-only send (no per-address mailbox, no
782
+ mailbox fee); 'mailbox' provisions a real dedicated mailbox per address
783
+ (better deliverability + per-address warmup). Only applies when a domain
784
+ is first set up — use it on a new ``from_domain``.
770
785
  """
771
786
  return self.call_tool(
772
787
  "/v1/tools/email/send",
773
- _build_email_payload(to, subject, body, from_domain, from_mailbox, from_name, kwargs, reply_to_email_id=reply_to_email_id),
788
+ _build_email_payload(to, subject, body, from_domain, from_mailbox, from_name, kwargs, reply_to_email_id=reply_to_email_id, mailbox_mode=mailbox_mode),
774
789
  )
775
790
 
776
791
  async def aemail(
@@ -783,6 +798,7 @@ class OneShotClient:
783
798
  from_mailbox: Optional[str] = None,
784
799
  from_name: Optional[str] = None,
785
800
  reply_to_email_id: Optional[str] = None,
801
+ mailbox_mode: Optional[str] = None,
786
802
  **kwargs: Any,
787
803
  ) -> Any:
788
804
  """Send an email. Async.
@@ -790,10 +806,13 @@ class OneShotClient:
790
806
  Set ``reply_to_email_id`` (an inbound email id from ``ainbox()``) to
791
807
  reply within a thread — ``to``/``subject`` are then derived from the
792
808
  inbound message unless given.
809
+
810
+ ``mailbox_mode`` ('relay' | 'mailbox', default 'relay') selects how a
811
+ domain sends — see ``email()`` for details.
793
812
  """
794
813
  return await self.acall_tool(
795
814
  "/v1/tools/email/send",
796
- _build_email_payload(to, subject, body, from_domain, from_mailbox, from_name, kwargs, reply_to_email_id=reply_to_email_id),
815
+ _build_email_payload(to, subject, body, from_domain, from_mailbox, from_name, kwargs, reply_to_email_id=reply_to_email_id, mailbox_mode=mailbox_mode),
797
816
  )
798
817
 
799
818
  def voice(self, objective: str, target_number: str, **kwargs: Any) -> Any:
@@ -968,21 +987,27 @@ class OneShotClient:
968
987
  value_tag: Optional[dict[str, Any]] = None,
969
988
  *,
970
989
  request_id: Optional[str] = None,
990
+ goal_id: Optional[str] = None,
971
991
  ) -> dict:
972
- """Tag a receipt with a value for RoCS computation. Blocking.
992
+ """Tag business value for RoCS computation. Blocking.
973
993
 
974
- Identify the receipt by ``receipt_id`` (``rcpt_…``) or, equivalently, by
975
- the ``request_id`` returned from the originating tool call the API
976
- resolves a ``request_id`` via the receipt's ``job_id``, so you can tag
977
- value without a prior receipts lookup.
994
+ Address the value three ways: by ``receipt_id`` (``rcpt_…``); by the
995
+ ``request_id`` returned from the originating tool call (the API resolves
996
+ it via the receipt's ``job_id``); or by ``goal_id`` — a correlation key
997
+ that attributes a whole *cadence's* outcome (a closed deal across the
998
+ intro email + follow-ups + find/verify/enrich calls sharing
999
+ ``decisionContext.goalId``) in one call. Read goal-level rollups back via
1000
+ ``rocs_by_goal``.
978
1001
 
979
1002
  ``value_tag`` shape: ``{"type": ..., "amount": ..., "label": ...}``.
980
1003
  ``type`` must be one of ``revenue``, ``lead``, ``conversion``,
981
- ``savings``, ``engagement``. Tags are stored as ``pending`` until a
982
- judge service confirms them against inbound signals.
1004
+ ``savings``, ``engagement``. Stored as ``pending`` until a judge service
1005
+ confirms them against inbound signals.
983
1006
  """
984
1007
  return asyncio.get_event_loop().run_until_complete(
985
- self.atag_receipt_value(receipt_id, value_tag, request_id=request_id)
1008
+ self.atag_receipt_value(
1009
+ receipt_id, value_tag, request_id=request_id, goal_id=goal_id
1010
+ )
986
1011
  )
987
1012
 
988
1013
  async def atag_receipt_value(
@@ -991,15 +1016,24 @@ class OneShotClient:
991
1016
  value_tag: Optional[dict[str, Any]] = None,
992
1017
  *,
993
1018
  request_id: Optional[str] = None,
1019
+ goal_id: Optional[str] = None,
994
1020
  ) -> dict:
995
- """Tag a receipt with a value for RoCS computation. Async."""
1021
+ """Tag business value for RoCS computation. Async."""
1022
+ if not isinstance(value_tag, dict) or not value_tag.get("type"):
1023
+ raise ValidationError("value_tag.type is required", "value_tag.type")
1024
+
1025
+ # Cadence-level: route to the outcome ledger by correlation key.
1026
+ if goal_id and not receipt_id and not request_id:
1027
+ return await self.acall_free_post(
1028
+ "/v1/analytics/outcomes",
1029
+ {"goal_id": goal_id, **value_tag},
1030
+ )
1031
+
996
1032
  target = receipt_id or request_id
997
1033
  if not target:
998
1034
  raise ValidationError(
999
- "receipt_id or request_id is required", "receipt_id"
1035
+ "receipt_id, request_id, or goal_id is required", "receipt_id"
1000
1036
  )
1001
- if not isinstance(value_tag, dict) or not value_tag.get("type"):
1002
- raise ValidationError("value_tag.type is required", "value_tag.type")
1003
1037
  return await self.acall_free_patch(
1004
1038
  f"/v1/analytics/receipts/{target}/value",
1005
1039
  value_tag,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "oneshot-python"
3
- version = "0.14.0"
3
+ version = "0.16.0"
4
4
  description = "Core Python SDK for the OneShot API — HTTP client with x402 payment handling"
5
5
  readme = {text = "Core Python SDK for the OneShot API", content-type = "text/plain"}
6
6
  license = "MIT"
@@ -46,6 +46,18 @@ def test_payload_passes_extra_kwargs():
46
46
  assert p["wait"] is True
47
47
 
48
48
 
49
+ def test_payload_forwards_mailbox_mode():
50
+ # mailbox_mode is an explicit keyword-only param, forwarded into the send body so
51
+ # the API's per-domain provider override + fee logic sees it.
52
+ p = _build_email_payload("r@x.com", "S", "B", "acme.com", None, None, {}, mailbox_mode="mailbox")
53
+ assert p["mailbox_mode"] == "mailbox"
54
+
55
+
56
+ def test_payload_omits_mailbox_mode_when_absent():
57
+ p = _build_email_payload("r@x.com", "S", "B", "acme.com", None, None, {})
58
+ assert "mailbox_mode" not in p
59
+
60
+
49
61
  # ── reply threading (reply_to_email_id) ───────────────────────────────────
50
62
 
51
63
  def test_reply_payload_omits_to_and_subject_when_derivable():
File without changes