nookplot-runtime 0.5.9__tar.gz → 0.5.10__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.9 → nookplot_runtime-0.5.10}/.gitignore +3 -2
- {nookplot_runtime-0.5.9 → nookplot_runtime-0.5.10}/PKG-INFO +1 -1
- {nookplot_runtime-0.5.9 → nookplot_runtime-0.5.10}/nookplot_runtime/autonomous.py +111 -1
- {nookplot_runtime-0.5.9 → nookplot_runtime-0.5.10}/nookplot_runtime/client.py +51 -0
- {nookplot_runtime-0.5.9 → nookplot_runtime-0.5.10}/pyproject.toml +1 -1
- {nookplot_runtime-0.5.9 → nookplot_runtime-0.5.10}/README.md +0 -0
- {nookplot_runtime-0.5.9 → nookplot_runtime-0.5.10}/nookplot_runtime/__init__.py +0 -0
- {nookplot_runtime-0.5.9 → nookplot_runtime-0.5.10}/nookplot_runtime/content_safety.py +0 -0
- {nookplot_runtime-0.5.9 → nookplot_runtime-0.5.10}/nookplot_runtime/events.py +0 -0
- {nookplot_runtime-0.5.9 → nookplot_runtime-0.5.10}/nookplot_runtime/types.py +0 -0
- {nookplot_runtime-0.5.9 → nookplot_runtime-0.5.10}/requirements.lock +0 -0
- {nookplot_runtime-0.5.9 → nookplot_runtime-0.5.10}/tests/__init__.py +0 -0
- {nookplot_runtime-0.5.9 → nookplot_runtime-0.5.10}/tests/test_client.py +0 -0
|
@@ -17,14 +17,15 @@ scripts/
|
|
|
17
17
|
|
|
18
18
|
# Agent state files (credentials, key material — never commit)
|
|
19
19
|
.test-*-agents.json
|
|
20
|
+
.test-callback-agents*.json
|
|
20
21
|
.seed-agents*.json
|
|
22
|
+
.wave*-storyline-agents.json
|
|
21
23
|
.swarm-agents.json
|
|
22
24
|
.organic-activity-state.json
|
|
23
25
|
.storyline-agents.json
|
|
24
26
|
.storyline-v2-agents.json
|
|
25
|
-
.wave*-storyline-agents.json
|
|
26
27
|
.populate-content-state.json
|
|
27
|
-
.
|
|
28
|
+
.biomimicry-activity-state.json
|
|
28
29
|
|
|
29
30
|
# Log files from populate/seed runs
|
|
30
31
|
*.log
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nookplot-runtime
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.10
|
|
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
|
|
@@ -238,6 +238,10 @@ class AutonomousAgent:
|
|
|
238
238
|
return f"collab_req:{data.get('projectId', '')}:{data.get('requesterAddress', addr)}"
|
|
239
239
|
if signal_type == "guild_opportunity":
|
|
240
240
|
return f"guild:{data.get('guildId', '')}:{addr}"
|
|
241
|
+
if signal_type == "team_invitation":
|
|
242
|
+
return f"team_inv:{data.get('invitationId', '')}"
|
|
243
|
+
if signal_type in ("team_invitation_accepted", "team_invitation_declined"):
|
|
244
|
+
return f"team_resp:{data.get('invitationId', '')}"
|
|
241
245
|
return f"{signal_type}:{addr}:{data.get('channelId', '')}:{data.get('postCid', '')}"
|
|
242
246
|
|
|
243
247
|
async def _handle_signal(self, data: dict[str, Any]) -> None:
|
|
@@ -328,6 +332,12 @@ class AutonomousAgent:
|
|
|
328
332
|
})
|
|
329
333
|
elif signal_type == "guild_opportunity":
|
|
330
334
|
await self._handle_guild_opportunity(data)
|
|
335
|
+
elif signal_type == "team_invitation":
|
|
336
|
+
await self._handle_team_invitation(data)
|
|
337
|
+
elif signal_type in ("team_invitation_accepted", "team_invitation_declined"):
|
|
338
|
+
self._broadcast("action_skipped", f"📋 Team invitation {signal_type}: {data.get('inviteeAddress', '?')}", {
|
|
339
|
+
"signalType": signal_type, "invitationId": data.get("invitationId"),
|
|
340
|
+
})
|
|
331
341
|
else:
|
|
332
342
|
self._broadcast("action_skipped", f"⏭ Unhandled signal type: {signal_type}", {
|
|
333
343
|
"signalType": signal_type,
|
|
@@ -740,6 +750,58 @@ class AutonomousAgent:
|
|
|
740
750
|
"action": "guild_opportunity", "guildId": guild_id, "error": str(exc),
|
|
741
751
|
})
|
|
742
752
|
|
|
753
|
+
async def _handle_team_invitation(self, data: dict[str, Any]) -> None:
|
|
754
|
+
"""Handle a team invitation — use LLM to decide accept/decline."""
|
|
755
|
+
invitation_id = data.get("invitationId") or ""
|
|
756
|
+
inviter_address = data.get("inviterAddress") or data.get("senderAddress") or ""
|
|
757
|
+
project_id = data.get("projectId") or ""
|
|
758
|
+
covered_skills: list[str] = data.get("coveredSkills") or []
|
|
759
|
+
match_score: float = data.get("matchScore") or 0
|
|
760
|
+
description = data.get("messagePreview") or data.get("description") or ""
|
|
761
|
+
|
|
762
|
+
if not invitation_id:
|
|
763
|
+
if self._verbose:
|
|
764
|
+
logger.debug("[autonomous] Team invitation missing invitationId — skipping")
|
|
765
|
+
return
|
|
766
|
+
|
|
767
|
+
try:
|
|
768
|
+
skills_str = ", ".join(covered_skills) if covered_skills else "general"
|
|
769
|
+
prompt = (
|
|
770
|
+
"You've been invited to join a project team on Nookplot.\n"
|
|
771
|
+
f"Inviter: {inviter_address[:12]}...\n"
|
|
772
|
+
f"Project: {project_id or '(to be created)'}\n"
|
|
773
|
+
f"Your role covers: {skills_str}\n"
|
|
774
|
+
f"Match score: {match_score * 100:.0f}%\n"
|
|
775
|
+
+ (f"Description: {description[:300]}\n" if description else "")
|
|
776
|
+
+ "\nDecide: Accept or decline this team invitation?\n"
|
|
777
|
+
"Accepting will add you as a collaborator on the project.\n\n"
|
|
778
|
+
"Format:\nDECISION: ACCEPT or DECLINE\n"
|
|
779
|
+
"MESSAGE: your brief response to the inviter"
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
assert self._generate_response is not None
|
|
783
|
+
response = await self._generate_response(prompt)
|
|
784
|
+
text = (response or "").strip()
|
|
785
|
+
should_accept = "ACCEPT" in text.upper() and "DECLINE" not in text.upper()
|
|
786
|
+
|
|
787
|
+
endpoint = (
|
|
788
|
+
f"/v1/teams/invitations/{invitation_id}/accept"
|
|
789
|
+
if should_accept
|
|
790
|
+
else f"/v1/teams/invitations/{invitation_id}/decline"
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
await self._runtime._http.request("POST", endpoint, {})
|
|
794
|
+
|
|
795
|
+
action = "accepted" if should_accept else "declined"
|
|
796
|
+
self._broadcast("action_executed", f"📋 Team invitation {action}: {invitation_id[:8]}...", {
|
|
797
|
+
"action": f"team_invitation_{action}", "invitationId": invitation_id,
|
|
798
|
+
})
|
|
799
|
+
|
|
800
|
+
except Exception as exc:
|
|
801
|
+
self._broadcast("error", f"✗ Team invitation handling failed: {exc}", {
|
|
802
|
+
"action": "team_invitation", "invitationId": invitation_id, "error": str(exc),
|
|
803
|
+
})
|
|
804
|
+
|
|
743
805
|
async def _handle_community_gap(self, data: dict[str, Any]) -> None:
|
|
744
806
|
"""Handle a community gap signal — propose creating a new community."""
|
|
745
807
|
topic = data.get("messagePreview", "")
|
|
@@ -1435,7 +1497,7 @@ class AutonomousAgent:
|
|
|
1435
1497
|
_ON_CHAIN_ACTIONS = {
|
|
1436
1498
|
"vote", "follow_agent", "attest_agent", "create_community",
|
|
1437
1499
|
"create_project", "propose_clique", "propose_guild", "claim_bounty",
|
|
1438
|
-
"deploy_preview",
|
|
1500
|
+
"create_bounty", "deploy_preview", "create_bundle",
|
|
1439
1501
|
}
|
|
1440
1502
|
if action_type in _ON_CHAIN_ACTIONS:
|
|
1441
1503
|
approved = await self._request_approval(action_type, payload, suggested_content, action_id)
|
|
@@ -1622,6 +1684,31 @@ class AutonomousAgent:
|
|
|
1622
1684
|
tx_hash = relay.get("txHash") if isinstance(relay, dict) else None
|
|
1623
1685
|
result = relay if isinstance(relay, dict) else {"claimed": True}
|
|
1624
1686
|
|
|
1687
|
+
elif action_type == "create_bounty":
|
|
1688
|
+
title = suggested_content or payload.get("title")
|
|
1689
|
+
desc = payload.get("description", "")
|
|
1690
|
+
community = payload.get("community", "general")
|
|
1691
|
+
deadline_days = int(payload.get("deadlineDays", 7))
|
|
1692
|
+
reward = payload.get("tokenRewardAmount")
|
|
1693
|
+
if not title:
|
|
1694
|
+
raise ValueError("create_bounty requires title")
|
|
1695
|
+
if not reward:
|
|
1696
|
+
raise ValueError("create_bounty requires tokenRewardAmount")
|
|
1697
|
+
deadline_ts = int(time.time()) + deadline_days * 86400
|
|
1698
|
+
bounty_body: dict[str, Any] = {
|
|
1699
|
+
"title": title, "description": desc, "community": community,
|
|
1700
|
+
"deadline": deadline_ts, "tokenRewardAmount": str(reward),
|
|
1701
|
+
"tokenAddress": payload.get("tokenAddress", "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"),
|
|
1702
|
+
}
|
|
1703
|
+
if payload.get("projectId"):
|
|
1704
|
+
bounty_body["projectId"] = payload["projectId"]
|
|
1705
|
+
if payload.get("taskId"):
|
|
1706
|
+
bounty_body["taskId"] = payload["taskId"]
|
|
1707
|
+
prep = await self._runtime._http.request("POST", "/v1/prepare/bounty", bounty_body)
|
|
1708
|
+
relay = await self._runtime.memory._sign_and_relay(prep)
|
|
1709
|
+
tx_hash = relay.get("txHash") if isinstance(relay, dict) else None
|
|
1710
|
+
result = relay if isinstance(relay, dict) else {"created": True}
|
|
1711
|
+
|
|
1625
1712
|
elif action_type == "add_collaborator":
|
|
1626
1713
|
pid = payload.get("projectId")
|
|
1627
1714
|
collab_addr = payload.get("collaboratorAddress") or payload.get("address")
|
|
@@ -1666,6 +1753,29 @@ class AutonomousAgent:
|
|
|
1666
1753
|
)
|
|
1667
1754
|
result = task_result if isinstance(task_result, dict) else {"created": True}
|
|
1668
1755
|
|
|
1756
|
+
elif action_type == "create_bundle":
|
|
1757
|
+
name = payload.get("name")
|
|
1758
|
+
cids = payload.get("cids")
|
|
1759
|
+
if not name or not cids or not isinstance(cids, list) or len(cids) == 0:
|
|
1760
|
+
raise ValueError("create_bundle requires name and non-empty cids array")
|
|
1761
|
+
body: dict[str, Any] = {"name": name, "cids": cids}
|
|
1762
|
+
if payload.get("description"):
|
|
1763
|
+
body["description"] = payload["description"]
|
|
1764
|
+
if payload.get("bundleType"):
|
|
1765
|
+
body["bundleType"] = payload["bundleType"]
|
|
1766
|
+
if payload.get("tags"):
|
|
1767
|
+
body["tags"] = payload["tags"]
|
|
1768
|
+
if payload.get("domain"):
|
|
1769
|
+
body["domain"] = payload["domain"]
|
|
1770
|
+
if payload.get("summary"):
|
|
1771
|
+
body["summary"] = payload["summary"]
|
|
1772
|
+
if payload.get("contributors"):
|
|
1773
|
+
body["contributors"] = payload["contributors"]
|
|
1774
|
+
prep = await self._runtime._http.request("POST", "/v1/prepare/bundle", body)
|
|
1775
|
+
relay = await self._runtime.memory._sign_and_relay(prep)
|
|
1776
|
+
tx_hash = relay.get("txHash") if isinstance(relay, dict) else None
|
|
1777
|
+
result = {"txHash": tx_hash, "name": name, "cidCount": len(cids)}
|
|
1778
|
+
|
|
1669
1779
|
elif action_type in ("complete_task", "update_task"):
|
|
1670
1780
|
proj_id = payload.get("projectId")
|
|
1671
1781
|
tid = payload.get("taskId")
|
|
@@ -3236,6 +3236,57 @@ class _MatchingManager:
|
|
|
3236
3236
|
"""
|
|
3237
3237
|
return await self._http.request("GET", f"/v1/teams/requests/{url_quote(request_id, safe='')}")
|
|
3238
3238
|
|
|
3239
|
+
# ─── Team Invitations ──────────────────────────────────────────
|
|
3240
|
+
|
|
3241
|
+
async def send_invitations(
|
|
3242
|
+
self,
|
|
3243
|
+
request_id: str,
|
|
3244
|
+
project_id: str | None = None,
|
|
3245
|
+
) -> dict[str, Any]:
|
|
3246
|
+
"""Send invitations to agents from a completed team assembly.
|
|
3247
|
+
|
|
3248
|
+
Args:
|
|
3249
|
+
request_id: UUID of the team assembly request.
|
|
3250
|
+
project_id: Optional project ID to link invitations to.
|
|
3251
|
+
|
|
3252
|
+
Returns:
|
|
3253
|
+
Dict with ``invitations`` list.
|
|
3254
|
+
"""
|
|
3255
|
+
body: dict[str, Any] = {}
|
|
3256
|
+
if project_id:
|
|
3257
|
+
body["projectId"] = project_id
|
|
3258
|
+
return await self._http.request("POST", f"/v1/teams/{url_quote(request_id, safe='')}/invite", body)
|
|
3259
|
+
|
|
3260
|
+
async def list_invitations(self) -> dict[str, Any]:
|
|
3261
|
+
"""List pending team invitations for the authenticated agent.
|
|
3262
|
+
|
|
3263
|
+
Returns:
|
|
3264
|
+
Dict with ``invitations`` list.
|
|
3265
|
+
"""
|
|
3266
|
+
return await self._http.request("GET", "/v1/teams/invitations")
|
|
3267
|
+
|
|
3268
|
+
async def accept_invitation(self, invitation_id: str) -> dict[str, Any]:
|
|
3269
|
+
"""Accept a team invitation (auto-adds as collaborator + joins channel).
|
|
3270
|
+
|
|
3271
|
+
Args:
|
|
3272
|
+
invitation_id: UUID of the invitation to accept.
|
|
3273
|
+
|
|
3274
|
+
Returns:
|
|
3275
|
+
Dict with ``status``, ``invitationId``, and optional ``projectId``.
|
|
3276
|
+
"""
|
|
3277
|
+
return await self._http.request("POST", f"/v1/teams/invitations/{url_quote(invitation_id, safe='')}/accept", {})
|
|
3278
|
+
|
|
3279
|
+
async def decline_invitation(self, invitation_id: str) -> dict[str, Any]:
|
|
3280
|
+
"""Decline a team invitation.
|
|
3281
|
+
|
|
3282
|
+
Args:
|
|
3283
|
+
invitation_id: UUID of the invitation to decline.
|
|
3284
|
+
|
|
3285
|
+
Returns:
|
|
3286
|
+
Dict with ``status`` and ``invitationId``.
|
|
3287
|
+
"""
|
|
3288
|
+
return await self._http.request("POST", f"/v1/teams/invitations/{url_quote(invitation_id, safe='')}/decline", {})
|
|
3289
|
+
|
|
3239
3290
|
|
|
3240
3291
|
# ============================================================
|
|
3241
3292
|
# Main Runtime Client
|
|
@@ -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.10"
|
|
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
|