pushary 2.2.1__tar.gz → 2.3.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 (26) hide show
  1. {pushary-2.2.1 → pushary-2.3.0}/PKG-INFO +6 -1
  2. {pushary-2.2.1 → pushary-2.3.0}/README.md +5 -0
  3. {pushary-2.2.1 → pushary-2.3.0}/pyproject.toml +1 -1
  4. {pushary-2.2.1 → pushary-2.3.0}/src/pushary/__init__.py +3 -1
  5. {pushary-2.2.1 → pushary-2.3.0}/src/pushary/client.py +2 -0
  6. pushary-2.3.0/src/pushary/reminders.py +25 -0
  7. pushary-2.3.0/tests/test_reminders.py +17 -0
  8. {pushary-2.2.1 → pushary-2.3.0}/.gitignore +0 -0
  9. {pushary-2.2.1 → pushary-2.3.0}/LICENSE +0 -0
  10. {pushary-2.2.1 → pushary-2.3.0}/examples/agno/README.md +0 -0
  11. {pushary-2.2.1 → pushary-2.3.0}/examples/dify/README.md +0 -0
  12. {pushary-2.2.1 → pushary-2.3.0}/examples/llamaindex/README.md +0 -0
  13. {pushary-2.2.1 → pushary-2.3.0}/examples/microsoft/README.md +0 -0
  14. {pushary-2.2.1 → pushary-2.3.0}/examples/strands/README.md +0 -0
  15. {pushary-2.2.1 → pushary-2.3.0}/src/pushary/_types.py +0 -0
  16. {pushary-2.2.1 → pushary-2.3.0}/src/pushary/_util.py +0 -0
  17. {pushary-2.2.1 → pushary-2.3.0}/src/pushary/adapters.py +0 -0
  18. {pushary-2.2.1 → pushary-2.3.0}/src/pushary/decisions.py +0 -0
  19. {pushary-2.2.1 → pushary-2.3.0}/src/pushary/errors.py +0 -0
  20. {pushary-2.2.1 → pushary-2.3.0}/src/pushary/keys.py +0 -0
  21. {pushary-2.2.1 → pushary-2.3.0}/src/pushary/py.typed +0 -0
  22. {pushary-2.2.1 → pushary-2.3.0}/src/pushary/webhook.py +0 -0
  23. {pushary-2.2.1 → pushary-2.3.0}/tests/test_adapters.py +0 -0
  24. {pushary-2.2.1 → pushary-2.3.0}/tests/test_agent.py +0 -0
  25. {pushary-2.2.1 → pushary-2.3.0}/tests/test_client.py +0 -0
  26. {pushary-2.2.1 → pushary-2.3.0}/tests/test_webhook.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pushary
3
- Version: 2.2.1
3
+ Version: 2.3.0
4
4
  Summary: The decision layer for AI agents. Your agent asks, your user decides on their phone.
5
5
  Project-URL: Homepage, https://pushary.com
6
6
  Project-URL: Documentation, https://pushary.com/docs
@@ -291,3 +291,8 @@ These keys intentionally differ from 2.0. Do not replay completed operations dur
291
291
  ### Upgrading to 2.2
292
292
 
293
293
  `ApprovalAsk` accepts `context`. The shared approval gate sends it with the decision, so the approver can read what the notification cannot fit, such as a tool's full arguments. Context is part of the approval identity when set. An ask without context keeps the key it had in 2.1, so pending approvals carry over.
294
+ ## Personal reminders
295
+
296
+ On an active Agent workspace, `client.reminders.schedule("Check the deploy", in_minutes=30)` saves a one-time reminder that survives the agent exiting. Use `at` with an explicit UTC offset for calendar times, up to 30 days away. `client.reminders.list()` lists your pending reminders; `client.reminders.cancel(reminder_id)` cancels one before dispatch starts. Confirm the returned `reminder.fireAt`. Delivery requires a connected phone and can be about a minute late. These personal reminders do not schedule agent work or Partner customer campaigns.
297
+
298
+ Personal reminder bodies must fit 180 UTF-16 units after secret redaction (most emoji use two); titles must fit 100. Oversized text is rejected. Listing and cancelling accepted reminders remain available when billing access closes.
@@ -263,3 +263,8 @@ These keys intentionally differ from 2.0. Do not replay completed operations dur
263
263
  ### Upgrading to 2.2
264
264
 
265
265
  `ApprovalAsk` accepts `context`. The shared approval gate sends it with the decision, so the approver can read what the notification cannot fit, such as a tool's full arguments. Context is part of the approval identity when set. An ask without context keeps the key it had in 2.1, so pending approvals carry over.
266
+ ## Personal reminders
267
+
268
+ On an active Agent workspace, `client.reminders.schedule("Check the deploy", in_minutes=30)` saves a one-time reminder that survives the agent exiting. Use `at` with an explicit UTC offset for calendar times, up to 30 days away. `client.reminders.list()` lists your pending reminders; `client.reminders.cancel(reminder_id)` cancels one before dispatch starts. Confirm the returned `reminder.fireAt`. Delivery requires a connected phone and can be about a minute late. These personal reminders do not schedule agent work or Partner customer campaigns.
269
+
270
+ Personal reminder bodies must fit 180 UTF-16 units after secret redaction (most emoji use two); titles must fit 100. Oversized text is rejected. Listing and cancelling accepted reminders remain available when billing access closes.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pushary"
7
- version = "2.2.1"
7
+ version = "2.3.0"
8
8
  description = "The decision layer for AI agents. Your agent asks, your user decides on their phone."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -19,15 +19,17 @@ from ._util import deterministic_key, is_approved
19
19
  from .client import PusharyServer
20
20
  from .decisions import DecisionsResource
21
21
  from .keys import KeysResource
22
+ from .reminders import RemindersResource
22
23
  from .errors import PusharyError
23
24
  from .webhook import SIGNATURE_HEADER, parse_decision_callback, verify_webhook_signature
24
25
 
25
- __version__ = "2.2.1"
26
+ __version__ = "2.3.0"
26
27
 
27
28
  __all__ = [
28
29
  "PusharyServer",
29
30
  "DecisionsResource",
30
31
  "KeysResource",
32
+ "RemindersResource",
31
33
  "PusharyError",
32
34
  "verify_webhook_signature",
33
35
  "parse_decision_callback",
@@ -15,6 +15,7 @@ from urllib.parse import quote, urlencode
15
15
 
16
16
  from .decisions import DecisionsResource
17
17
  from .keys import KeysResource
18
+ from .reminders import RemindersResource
18
19
  from .errors import PusharyError
19
20
 
20
21
  DEFAULT_BASE_URL = "https://pushary.com/api/v1/server"
@@ -49,6 +50,7 @@ class PusharyServer:
49
50
  self._timeout = DEFAULT_TIMEOUT_SECONDS
50
51
  self.decisions = DecisionsResource(self._request)
51
52
  self.keys = KeysResource(self._request)
53
+ self.reminders = RemindersResource(self._request)
52
54
 
53
55
  def evaluate_authorization(
54
56
  self,
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+ from typing import Callable, Dict, Optional
3
+
4
+ RequestFn = Callable[..., Dict[str, object]]
5
+
6
+
7
+ class RemindersResource:
8
+ def __init__(self, request: RequestFn) -> None:
9
+ self._request = request
10
+
11
+ def schedule(
12
+ self, body: str, *, in_minutes: Optional[int] = None, at: Optional[str] = None,
13
+ title: Optional[str] = None, agent_name: Optional[str] = None,
14
+ session_id: Optional[str] = None, machine_id: Optional[str] = None,
15
+ env: Optional[str] = None,
16
+ ) -> Dict[str, object]:
17
+ values = {"body": body, "inMinutes": in_minutes, "at": at, "title": title,
18
+ "agentName": agent_name, "sessionId": session_id, "machineId": machine_id, "env": env}
19
+ return self._request("POST", "/reminders", body={key: value for key, value in values.items() if value is not None})
20
+
21
+ def list(self) -> Dict[str, object]:
22
+ return self._request("GET", "/reminders")
23
+
24
+ def cancel(self, reminder_id: str) -> Dict[str, object]:
25
+ return self._request("POST", "/reminders", body={"cancelReminderId": reminder_id})
@@ -0,0 +1,17 @@
1
+ import unittest
2
+ from unittest import mock
3
+ from pushary import PusharyServer
4
+
5
+
6
+ class ReminderTests(unittest.TestCase):
7
+ def test_schedule_list_and_cancel_use_authenticated_reminder_resource(self):
8
+ with mock.patch.object(PusharyServer, '_request', return_value={'pending': []}) as request:
9
+ client = PusharyServer('pk_test.secret')
10
+ client.reminders.schedule('Check deploy', in_minutes=30)
11
+ client.reminders.list()
12
+ client.reminders.cancel('id')
13
+ self.assertEqual(request.call_args_list, [
14
+ mock.call('POST', '/reminders', body={'body': 'Check deploy', 'inMinutes': 30}),
15
+ mock.call('GET', '/reminders'),
16
+ mock.call('POST', '/reminders', body={'cancelReminderId': 'id'}),
17
+ ])
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes