claritty-sdk 2.0.2__tar.gz → 2.0.4__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 (75) hide show
  1. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/PKG-INFO +1 -1
  2. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/executor_client.py +6 -1
  3. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/platform_creds.py +8 -1
  4. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/runtime/platform_client.py +6 -1
  5. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/runtime/tool_loop.py +16 -7
  6. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk.egg-info/PKG-INFO +1 -1
  7. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk.egg-info/SOURCES.txt +1 -0
  8. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/pyproject.toml +1 -1
  9. claritty_sdk-2.0.4/tests/test_integration_broker_dispatch.py +60 -0
  10. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/README.md +0 -0
  11. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/__init__.py +0 -0
  12. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/cli_eval.py +0 -0
  13. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/cli_verify.py +0 -0
  14. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/context.py +0 -0
  15. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/decorators.py +0 -0
  16. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/enums.py +0 -0
  17. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/eval.py +0 -0
  18. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/executor.py +0 -0
  19. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/graph.py +0 -0
  20. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/__init__.py +0 -0
  21. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/__init__.pyi +0 -0
  22. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/airtable.pyi +0 -0
  23. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/anthropic.pyi +0 -0
  24. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/asana.pyi +0 -0
  25. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/calendly.pyi +0 -0
  26. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/catalog.py +0 -0
  27. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/clickup.pyi +0 -0
  28. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/client.py +0 -0
  29. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/discord.pyi +0 -0
  30. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/dropbox.pyi +0 -0
  31. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/github.pyi +0 -0
  32. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/gitlab.pyi +0 -0
  33. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/gmail.pyi +0 -0
  34. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/google_calendar.pyi +0 -0
  35. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/google_drive.pyi +0 -0
  36. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/hubspot.pyi +0 -0
  37. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/jira.pyi +0 -0
  38. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/linear.pyi +0 -0
  39. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/linkedin.pyi +0 -0
  40. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/mailchimp.pyi +0 -0
  41. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/mcp_client.py +0 -0
  42. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/microsoft_teams.pyi +0 -0
  43. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/notion.pyi +0 -0
  44. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/notion_mcp.pyi +0 -0
  45. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/openai.pyi +0 -0
  46. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/outlook.pyi +0 -0
  47. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/outlook_calendar.pyi +0 -0
  48. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/pipedrive.pyi +0 -0
  49. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/postgres.pyi +0 -0
  50. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/posthog.pyi +0 -0
  51. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/s3.pyi +0 -0
  52. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/salesforce.pyi +0 -0
  53. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/slack.pyi +0 -0
  54. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/stripe.pyi +0 -0
  55. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/trello.pyi +0 -0
  56. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/twilio.pyi +0 -0
  57. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/integrations/x_twitter.pyi +0 -0
  58. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/llm.py +0 -0
  59. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/manifest.py +0 -0
  60. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/registry.py +0 -0
  61. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/runtime/__init__.py +0 -0
  62. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/runtime/bootstrap.py +0 -0
  63. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/runtime/expressions.py +0 -0
  64. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/runtime/schema_validate.py +0 -0
  65. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/runtime/tracing.py +0 -0
  66. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/runtime/workflow_engine.py +0 -0
  67. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/trigger_manager.py +0 -0
  68. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk/validators.py +0 -0
  69. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk.egg-info/dependency_links.txt +0 -0
  70. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk.egg-info/entry_points.txt +0 -0
  71. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk.egg-info/requires.txt +0 -0
  72. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/claritty_sdk.egg-info/top_level.txt +0 -0
  73. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/setup.cfg +0 -0
  74. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/tests/test_graph_emitter.py +0 -0
  75. {claritty_sdk-2.0.2 → claritty_sdk-2.0.4}/tests/test_manifest_triggers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claritty-sdk
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: Official SDK for building apps on the Claritty Platform
5
5
  Author: Claritty Platform
6
6
  License: MIT
@@ -55,11 +55,16 @@ def execute_tool(
55
55
  os.environ.get("CLARITTY_CREDS_TIMEOUT_S", "30")
56
56
  )
57
57
  url = f"{base}/internal/integrations/tools/{integration_id}/{tool_id}/execute"
58
+ # Per-app scoping: the platform serves the connection bound to THIS app.
59
+ payload: Dict[str, Any] = {"userId": user_id, "arguments": arguments}
60
+ app_id = os.environ.get("CLARITY_APP_ID")
61
+ if app_id:
62
+ payload["appId"] = app_id
58
63
  try:
59
64
  resp = httpx.post(
60
65
  url,
61
66
  headers={"X-Claritty-Internal": secret},
62
- json={"userId": user_id, "arguments": arguments},
67
+ json=payload,
63
68
  timeout=timeout,
64
69
  )
65
70
  except httpx.HTTPError as e:
@@ -110,11 +110,18 @@ def fetch_for_user(integration_id: str, user_id: str) -> Credentials:
110
110
  import httpx
111
111
 
112
112
  url = f"{base}/internal/integrations/credentials/fetch"
113
+ # Per-app scoping: the platform resolves the connection bound to THIS app.
114
+ # CLARITY_APP_ID is injected per deployed app; omit when absent (legacy →
115
+ # the platform falls back to the user-scoped connection).
116
+ payload = {"userId": user_id, "integrationCatalogId": integration_id}
117
+ app_id = os.environ.get("CLARITY_APP_ID")
118
+ if app_id:
119
+ payload["appId"] = app_id
113
120
  try:
114
121
  with httpx.Client(timeout=_FETCH_TIMEOUT_S) as client:
115
122
  res = client.post(
116
123
  url,
117
- json={"userId": user_id, "integrationCatalogId": integration_id},
124
+ json=payload,
118
125
  headers={
119
126
  "X-Claritty-Internal": secret,
120
127
  "Content-Type": "application/json",
@@ -138,11 +138,16 @@ class PlatformClient:
138
138
  f"{self.base_url}/internal/integrations/"
139
139
  f"{integration_id}/{bare}/execute"
140
140
  )
141
+ # Per-app scoping: platform serves the connection bound to THIS app.
142
+ payload: Dict[str, Any] = {"userId": user_id, "arguments": arguments}
143
+ app_id = os.environ.get("CLARITY_APP_ID")
144
+ if app_id:
145
+ payload["appId"] = app_id
141
146
  try:
142
147
  with httpx.Client(timeout=self._timeout) as http:
143
148
  res = http.post(
144
149
  url,
145
- json={"userId": user_id, "arguments": arguments},
150
+ json=payload,
146
151
  headers={
147
152
  "X-Claritty-Internal": self._secret,
148
153
  "Content-Type": "application/json",
@@ -618,14 +618,23 @@ async def _dispatch_tool(
618
618
  return {"error": str(exc)}
619
619
 
620
620
  handler = _decorators.get_registered_tool(tool_id)
621
+ if handler is None and "." in tool_id and decl.source == "catalog":
622
+ # Integration-provided tool with no locally-registered handler → BROKER
623
+ # it through the platform executor: the platform holds the credential +
624
+ # provider REST code and runs the call server-side; the app only gets the
625
+ # result. This is the canonical secure path — the credential never
626
+ # reaches the app. (Phase 3.)
627
+ from ..integrations.catalog import (
628
+ get_provided_tool,
629
+ _make_platform_delegate,
630
+ )
631
+
632
+ integration_id = tool_id.split(".", 1)[0]
633
+ handler = get_provided_tool(tool_id) or _make_platform_delegate(
634
+ integration_id, tool_id
635
+ )
621
636
  if handler is None:
622
- return {
623
- "error": (
624
- f"no registered handler for tool {tool_id!r}; the agent "
625
- "may need to wait for Phase 3 (integration catalog) for "
626
- "this to be dispatched"
627
- )
628
- }
637
+ return {"error": f"no registered handler for tool {tool_id!r}"}
629
638
 
630
639
  tool_ctx = ToolCtx(
631
640
  user_id=agent_ctx.user_id,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claritty-sdk
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: Official SDK for building apps on the Claritty Platform
5
5
  Author: Claritty Platform
6
6
  License: MIT
@@ -69,4 +69,5 @@ claritty_sdk/runtime/tool_loop.py
69
69
  claritty_sdk/runtime/tracing.py
70
70
  claritty_sdk/runtime/workflow_engine.py
71
71
  tests/test_graph_emitter.py
72
+ tests/test_integration_broker_dispatch.py
72
73
  tests/test_manifest_triggers.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "claritty-sdk"
7
- version = "2.0.2"
7
+ version = "2.0.4"
8
8
  description = "Official SDK for building apps on the Claritty Platform"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -0,0 +1,60 @@
1
+ """Phase 3: the agent tool-loop must BROKER integration-provided tools through
2
+ the platform executor — the credential never reaches the app. A catalog-source
3
+ tool with no locally-registered handler is dispatched via the platform delegate
4
+ (PlatformClient.execute_integration_tool), and only its result is returned.
5
+ """
6
+ import asyncio
7
+
8
+ from claritty_sdk.runtime import tool_loop
9
+ from claritty_sdk.runtime import platform_client
10
+ from claritty_sdk.manifest import ToolDecl
11
+ from claritty_sdk.context import AgentContext
12
+
13
+
14
+ def _ctx():
15
+ return AgentContext(user_id="u1")
16
+
17
+
18
+ def test_integration_tool_is_brokered(monkeypatch):
19
+ seen = {}
20
+
21
+ def fake_exec(self, integration_id, tool_id, user_id, arguments):
22
+ seen.update(
23
+ integration_id=integration_id,
24
+ tool_id=tool_id,
25
+ user_id=user_id,
26
+ arguments=arguments,
27
+ )
28
+ return {"message_id": "m1"}
29
+
30
+ monkeypatch.setattr(
31
+ platform_client.PlatformClient, "execute_integration_tool", fake_exec
32
+ )
33
+
34
+ toolset = {
35
+ "gmail.send": ToolDecl(
36
+ id="gmail.send", source="catalog", handler="", input={}, output={}
37
+ )
38
+ }
39
+ out = asyncio.run(
40
+ tool_loop._dispatch_tool(
41
+ "gmail.send", {"to": "x@y.com"}, toolset, _ctx(), None
42
+ )
43
+ )
44
+ # Only the result comes back — no credential.
45
+ assert out == {"message_id": "m1"}
46
+ assert seen["integration_id"] == "gmail"
47
+ assert seen["user_id"] == "u1"
48
+ assert seen["arguments"] == {"to": "x@y.com"}
49
+
50
+
51
+ def test_unknown_custom_tool_still_errors():
52
+ toolset = {
53
+ "foo.bar": ToolDecl(
54
+ id="foo.bar", source="custom", handler="", input={}, output={}
55
+ )
56
+ }
57
+ out = asyncio.run(
58
+ tool_loop._dispatch_tool("foo.bar", {}, toolset, _ctx(), None)
59
+ )
60
+ assert "no registered handler" in out.get("error", "")
File without changes
File without changes