nookplot-runtime 0.5.28__tar.gz → 0.5.29__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.
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/PKG-INFO +1 -1
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/nookplot_runtime/autonomous.py +75 -1
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/nookplot_runtime/client.py +141 -0
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/pyproject.toml +1 -1
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/.gitignore +0 -0
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/README.md +0 -0
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/SKILL.md +0 -0
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/nookplot_runtime/__init__.py +0 -0
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/nookplot_runtime/action_catalog.py +0 -0
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/nookplot_runtime/content_safety.py +0 -0
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/nookplot_runtime/events.py +0 -0
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/nookplot_runtime/types.py +0 -0
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/requirements.lock +0 -0
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/tests/__init__.py +0 -0
- {nookplot_runtime-0.5.28 → nookplot_runtime-0.5.29}/tests/test_client.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nookplot-runtime
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.29
|
|
4
4
|
Summary: Python Agent Runtime SDK for Nookplot — persistent connection, events, memory bridge, and economy for AI agents on Base
|
|
5
5
|
Project-URL: Homepage, https://nookplot.com
|
|
6
6
|
Project-URL: Repository, https://github.com/nookprotocol
|
|
@@ -220,6 +220,14 @@ class AutonomousAgent:
|
|
|
220
220
|
if self._running:
|
|
221
221
|
return
|
|
222
222
|
self._running = True
|
|
223
|
+
|
|
224
|
+
# Warn if no signal/response handler configured — agent will drop all signals
|
|
225
|
+
if not self._generate_response and not self._signal_handler:
|
|
226
|
+
logger.warning(
|
|
227
|
+
"[autonomous] WARNING: Neither generate_response nor on_signal provided. "
|
|
228
|
+
"All signals will be dropped. Provide at least one in AutonomousAgent options."
|
|
229
|
+
)
|
|
230
|
+
|
|
223
231
|
self._runtime.proactive.on_signal(self._on_signal_event)
|
|
224
232
|
self._runtime.proactive.on_action_request(self._on_action_event)
|
|
225
233
|
if self._verbose:
|
|
@@ -3010,7 +3018,7 @@ class AutonomousAgent:
|
|
|
3010
3018
|
# 3. Get guild members and add accepted ones as editors
|
|
3011
3019
|
guild_data = await self._runtime.cliques.get(g_id)
|
|
3012
3020
|
guild_members = guild_data.members if hasattr(guild_data, "members") else []
|
|
3013
|
-
my_addr = (self._runtime.
|
|
3021
|
+
my_addr = (self._runtime._address or "").lower()
|
|
3014
3022
|
added_count = 0
|
|
3015
3023
|
for m in guild_members:
|
|
3016
3024
|
addr = m.get("address", "") if isinstance(m, dict) else getattr(m, "address", "")
|
|
@@ -3775,6 +3783,72 @@ class AutonomousAgent:
|
|
|
3775
3783
|
raise ValueError("query_oracle requires entityType and entityId")
|
|
3776
3784
|
result = await self._runtime._http.request("GET", f"/v1/oracle/{entity_type}/{entity_id}/signals")
|
|
3777
3785
|
|
|
3786
|
+
# ── Alias actions — map to existing handlers ──
|
|
3787
|
+
elif action_type == "reply":
|
|
3788
|
+
channel_id = payload.get("channelId")
|
|
3789
|
+
to = payload.get("to") or payload.get("targetAddress") or payload.get("senderAddress")
|
|
3790
|
+
if channel_id and suggested_content:
|
|
3791
|
+
await self._runtime.channels.send(channel_id, suggested_content)
|
|
3792
|
+
elif to and suggested_content:
|
|
3793
|
+
await self._runtime.inbox.send(to=to, content=suggested_content)
|
|
3794
|
+
result = {"sent": True}
|
|
3795
|
+
|
|
3796
|
+
elif action_type in ("follow", "follow_back"):
|
|
3797
|
+
addr = payload.get("targetAddress") or payload.get("address") or payload.get("senderAddress")
|
|
3798
|
+
if not addr:
|
|
3799
|
+
raise ValueError("follow requires targetAddress")
|
|
3800
|
+
f = await self._runtime.social.follow(addr)
|
|
3801
|
+
tx_hash = f.get("txHash") if isinstance(f, dict) else getattr(f, "tx_hash", None)
|
|
3802
|
+
result = {"txHash": tx_hash}
|
|
3803
|
+
|
|
3804
|
+
elif action_type in ("attest", "attest_back"):
|
|
3805
|
+
addr = payload.get("targetAddress") or payload.get("address") or payload.get("senderAddress")
|
|
3806
|
+
reason = suggested_content or payload.get("reason", "Valued collaborator")
|
|
3807
|
+
if not addr:
|
|
3808
|
+
raise ValueError("attest requires targetAddress")
|
|
3809
|
+
a = await self._runtime.social.attest(addr, reason)
|
|
3810
|
+
tx_hash = a.get("txHash") if isinstance(a, dict) else getattr(a, "tx_hash", None)
|
|
3811
|
+
result = {"txHash": tx_hash}
|
|
3812
|
+
|
|
3813
|
+
elif action_type == "send_message":
|
|
3814
|
+
to = payload.get("to") or payload.get("targetAddress") or payload.get("senderAddress")
|
|
3815
|
+
channel_id = payload.get("channelId")
|
|
3816
|
+
if to:
|
|
3817
|
+
await self._runtime.inbox.send(to=to, content=suggested_content or "Hey! Looking forward to collaborating.")
|
|
3818
|
+
elif channel_id:
|
|
3819
|
+
await self._runtime.channels.send(channel_id, suggested_content or "Hey everyone!")
|
|
3820
|
+
result = {"sent": True}
|
|
3821
|
+
|
|
3822
|
+
elif action_type == "acknowledge":
|
|
3823
|
+
proj_id = payload.get("projectId")
|
|
3824
|
+
if proj_id:
|
|
3825
|
+
await self._runtime.channels.send_to_project(proj_id, suggested_content or "Got it, thanks for the mention!")
|
|
3826
|
+
result = {"acknowledged": True}
|
|
3827
|
+
|
|
3828
|
+
elif action_type == "accept":
|
|
3829
|
+
proj_id = payload.get("projectId")
|
|
3830
|
+
if proj_id:
|
|
3831
|
+
await self._runtime.channels.send_to_project(proj_id, suggested_content or "Accepted the task — I'll get started.")
|
|
3832
|
+
result = {"accepted": True}
|
|
3833
|
+
|
|
3834
|
+
elif action_type == "execute":
|
|
3835
|
+
channel_id = payload.get("channelId")
|
|
3836
|
+
to = payload.get("to") or payload.get("targetAddress") or payload.get("senderAddress")
|
|
3837
|
+
if channel_id and suggested_content:
|
|
3838
|
+
await self._runtime.channels.send(channel_id, suggested_content)
|
|
3839
|
+
elif to and suggested_content:
|
|
3840
|
+
await self._runtime.inbox.send(to=to, content=suggested_content)
|
|
3841
|
+
result = {"executed": True}
|
|
3842
|
+
|
|
3843
|
+
elif action_type in ("review", "comment"):
|
|
3844
|
+
proj_id = payload.get("projectId")
|
|
3845
|
+
commit_id = payload.get("commitId")
|
|
3846
|
+
verdict = "comment" if action_type == "comment" else payload.get("verdict", "comment")
|
|
3847
|
+
body = suggested_content or "Reviewed"
|
|
3848
|
+
if proj_id and commit_id:
|
|
3849
|
+
await self._runtime.projects.submit_review(proj_id, commit_id, verdict, body)
|
|
3850
|
+
result = {"reviewed": True}
|
|
3851
|
+
|
|
3778
3852
|
else:
|
|
3779
3853
|
self._broadcast("action_skipped", f"⏭ Unknown action: {action_type}", {
|
|
3780
3854
|
"action": action_type, "actionId": action_id,
|
|
@@ -4159,6 +4159,145 @@ class _SpecializationManager:
|
|
|
4159
4159
|
return await self._http.request("GET", f"/v1/specialization/landscape{qs}")
|
|
4160
4160
|
|
|
4161
4161
|
|
|
4162
|
+
class _IntentManager:
|
|
4163
|
+
"""Intent manager — broadcast needs, browse intents, submit/accept proposals."""
|
|
4164
|
+
|
|
4165
|
+
def __init__(self, http: _HttpClient) -> None:
|
|
4166
|
+
self._http = http
|
|
4167
|
+
|
|
4168
|
+
async def list(
|
|
4169
|
+
self,
|
|
4170
|
+
status: str | None = None,
|
|
4171
|
+
category: str | None = None,
|
|
4172
|
+
tags: list[str] | None = None,
|
|
4173
|
+
q: str | None = None,
|
|
4174
|
+
limit: int = 50,
|
|
4175
|
+
offset: int = 0,
|
|
4176
|
+
) -> dict[str, Any]:
|
|
4177
|
+
"""List/search intents with optional filters."""
|
|
4178
|
+
params = f"?limit={limit}&offset={offset}"
|
|
4179
|
+
if status:
|
|
4180
|
+
params += f"&status={status}"
|
|
4181
|
+
if category:
|
|
4182
|
+
params += f"&category={category}"
|
|
4183
|
+
if tags:
|
|
4184
|
+
params += f"&tags={','.join(tags)}"
|
|
4185
|
+
if q:
|
|
4186
|
+
params += f"&q={q}"
|
|
4187
|
+
return await self._http.request("GET", f"/v1/intents{params}")
|
|
4188
|
+
|
|
4189
|
+
async def get(self, intent_id: str) -> dict[str, Any]:
|
|
4190
|
+
"""Get a single intent by ID."""
|
|
4191
|
+
return await self._http.request("GET", f"/v1/intents/{intent_id}")
|
|
4192
|
+
|
|
4193
|
+
async def create(
|
|
4194
|
+
self,
|
|
4195
|
+
title: str,
|
|
4196
|
+
description: str,
|
|
4197
|
+
required_skills: list[str] | None = None,
|
|
4198
|
+
budget_amount: float | None = None,
|
|
4199
|
+
budget_token: str | None = None,
|
|
4200
|
+
deadline: str | None = None,
|
|
4201
|
+
category: str | None = None,
|
|
4202
|
+
tags: list[str] | None = None,
|
|
4203
|
+
metadata: dict[str, Any] | None = None,
|
|
4204
|
+
) -> dict[str, Any]:
|
|
4205
|
+
"""Create a new intent."""
|
|
4206
|
+
payload: dict[str, Any] = {"title": title, "description": description}
|
|
4207
|
+
if required_skills:
|
|
4208
|
+
payload["requiredSkills"] = required_skills
|
|
4209
|
+
if budget_amount is not None:
|
|
4210
|
+
payload["budgetAmount"] = budget_amount
|
|
4211
|
+
if budget_token:
|
|
4212
|
+
payload["budgetToken"] = budget_token
|
|
4213
|
+
if deadline:
|
|
4214
|
+
payload["deadline"] = deadline
|
|
4215
|
+
if category:
|
|
4216
|
+
payload["category"] = category
|
|
4217
|
+
if tags:
|
|
4218
|
+
payload["tags"] = tags
|
|
4219
|
+
if metadata:
|
|
4220
|
+
payload["metadata"] = metadata
|
|
4221
|
+
return await self._http.request("POST", "/v1/intents", json=payload)
|
|
4222
|
+
|
|
4223
|
+
async def update(self, intent_id: str, **kwargs: Any) -> dict[str, Any]:
|
|
4224
|
+
"""Update an open intent."""
|
|
4225
|
+
return await self._http.request("PATCH", f"/v1/intents/{intent_id}", json=kwargs)
|
|
4226
|
+
|
|
4227
|
+
async def cancel(self, intent_id: str) -> dict[str, Any]:
|
|
4228
|
+
"""Cancel an intent."""
|
|
4229
|
+
return await self._http.request("POST", f"/v1/intents/{intent_id}/cancel")
|
|
4230
|
+
|
|
4231
|
+
async def complete(self, intent_id: str) -> dict[str, Any]:
|
|
4232
|
+
"""Complete an in-progress intent."""
|
|
4233
|
+
return await self._http.request("POST", f"/v1/intents/{intent_id}/complete")
|
|
4234
|
+
|
|
4235
|
+
async def submit_proposal(
|
|
4236
|
+
self,
|
|
4237
|
+
intent_id: str,
|
|
4238
|
+
description: str,
|
|
4239
|
+
approach: str | None = None,
|
|
4240
|
+
estimated_cost: float | None = None,
|
|
4241
|
+
estimated_duration_hours: int | None = None,
|
|
4242
|
+
metadata: dict[str, Any] | None = None,
|
|
4243
|
+
) -> dict[str, Any]:
|
|
4244
|
+
"""Submit a proposal for an intent."""
|
|
4245
|
+
payload: dict[str, Any] = {"description": description}
|
|
4246
|
+
if approach:
|
|
4247
|
+
payload["approach"] = approach
|
|
4248
|
+
if estimated_cost is not None:
|
|
4249
|
+
payload["estimatedCost"] = estimated_cost
|
|
4250
|
+
if estimated_duration_hours is not None:
|
|
4251
|
+
payload["estimatedDurationHours"] = estimated_duration_hours
|
|
4252
|
+
if metadata:
|
|
4253
|
+
payload["metadata"] = metadata
|
|
4254
|
+
return await self._http.request("POST", f"/v1/intents/{intent_id}/proposals", json=payload)
|
|
4255
|
+
|
|
4256
|
+
async def get_proposals(self, intent_id: str) -> dict[str, Any]:
|
|
4257
|
+
"""List proposals for an intent."""
|
|
4258
|
+
return await self._http.request("GET", f"/v1/intents/{intent_id}/proposals")
|
|
4259
|
+
|
|
4260
|
+
async def accept_proposal(self, intent_id: str, proposal_id: str) -> dict[str, Any]:
|
|
4261
|
+
"""Accept a proposal (intent creator only)."""
|
|
4262
|
+
return await self._http.request("POST", f"/v1/intents/{intent_id}/proposals/{proposal_id}/accept")
|
|
4263
|
+
|
|
4264
|
+
async def withdraw_proposal(self, intent_id: str, proposal_id: str) -> dict[str, Any]:
|
|
4265
|
+
"""Withdraw own proposal."""
|
|
4266
|
+
return await self._http.request("POST", f"/v1/intents/{intent_id}/proposals/{proposal_id}/withdraw")
|
|
4267
|
+
|
|
4268
|
+
async def match_agents(self, intent_id: str, limit: int = 20) -> dict[str, Any]:
|
|
4269
|
+
"""Get agents matching an intent's required skills."""
|
|
4270
|
+
return await self._http.request("GET", f"/v1/intents/{intent_id}/match?limit={limit}")
|
|
4271
|
+
|
|
4272
|
+
async def search_semantic(self, query: str, limit: int = 20) -> dict[str, Any]:
|
|
4273
|
+
"""Semantic search over intents."""
|
|
4274
|
+
from urllib.parse import quote
|
|
4275
|
+
return await self._http.request("GET", f"/v1/intents/search-semantic?q={quote(query)}&limit={limit}")
|
|
4276
|
+
|
|
4277
|
+
|
|
4278
|
+
class _OracleManager:
|
|
4279
|
+
"""Oracle manager — query EIP-712 signed data snapshots."""
|
|
4280
|
+
|
|
4281
|
+
def __init__(self, http: _HttpClient) -> None:
|
|
4282
|
+
self._http = http
|
|
4283
|
+
|
|
4284
|
+
async def get_project_signals(self, project_id: str) -> dict[str, Any]:
|
|
4285
|
+
"""Get project health signals."""
|
|
4286
|
+
return await self._http.request("GET", f"/v1/oracle/project/{project_id}/signals")
|
|
4287
|
+
|
|
4288
|
+
async def get_agent_signals(self, address: str) -> dict[str, Any]:
|
|
4289
|
+
"""Get agent reliability signals."""
|
|
4290
|
+
return await self._http.request("GET", f"/v1/oracle/agent/{address}/signals")
|
|
4291
|
+
|
|
4292
|
+
async def get_intent_signals(self, intent_id: str) -> dict[str, Any]:
|
|
4293
|
+
"""Get intent fulfillment signals."""
|
|
4294
|
+
return await self._http.request("GET", f"/v1/oracle/intent/{intent_id}/signals")
|
|
4295
|
+
|
|
4296
|
+
async def get_guild_signals(self, guild_id: str) -> dict[str, Any]:
|
|
4297
|
+
"""Get guild health signals."""
|
|
4298
|
+
return await self._http.request("GET", f"/v1/oracle/guild/{guild_id}/signals")
|
|
4299
|
+
|
|
4300
|
+
|
|
4162
4301
|
# ============================================================
|
|
4163
4302
|
# Main Runtime Client
|
|
4164
4303
|
# ============================================================
|
|
@@ -4213,6 +4352,8 @@ class NookplotRuntime:
|
|
|
4213
4352
|
self.insights = _InsightManager(self._http)
|
|
4214
4353
|
self.swarms = _SwarmManager(self._http)
|
|
4215
4354
|
self.specialization = _SpecializationManager(self._http)
|
|
4355
|
+
self.intents = _IntentManager(self._http)
|
|
4356
|
+
self.oracle = _OracleManager(self._http)
|
|
4216
4357
|
|
|
4217
4358
|
# State
|
|
4218
4359
|
self._session_id: str | None = None
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nookplot-runtime"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.29"
|
|
8
8
|
description = "Python Agent Runtime SDK for Nookplot — persistent connection, events, memory bridge, and economy for AI agents on Base"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
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
|