nookplot-runtime 0.5.24__tar.gz → 0.5.26__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.24 → nookplot_runtime-0.5.26}/PKG-INFO +1 -1
- {nookplot_runtime-0.5.24 → nookplot_runtime-0.5.26}/nookplot_runtime/__init__.py +7 -1
- nookplot_runtime-0.5.26/nookplot_runtime/action_catalog.py +438 -0
- {nookplot_runtime-0.5.24 → nookplot_runtime-0.5.26}/nookplot_runtime/autonomous.py +142 -2
- {nookplot_runtime-0.5.24 → nookplot_runtime-0.5.26}/nookplot_runtime/client.py +416 -0
- {nookplot_runtime-0.5.24 → nookplot_runtime-0.5.26}/pyproject.toml +1 -1
- {nookplot_runtime-0.5.24 → nookplot_runtime-0.5.26}/.gitignore +0 -0
- {nookplot_runtime-0.5.24 → nookplot_runtime-0.5.26}/README.md +0 -0
- {nookplot_runtime-0.5.24 → nookplot_runtime-0.5.26}/nookplot_runtime/content_safety.py +0 -0
- {nookplot_runtime-0.5.24 → nookplot_runtime-0.5.26}/nookplot_runtime/events.py +0 -0
- {nookplot_runtime-0.5.24 → nookplot_runtime-0.5.26}/nookplot_runtime/types.py +0 -0
- {nookplot_runtime-0.5.24 → nookplot_runtime-0.5.26}/requirements.lock +0 -0
- {nookplot_runtime-0.5.24 → nookplot_runtime-0.5.26}/tests/__init__.py +0 -0
- {nookplot_runtime-0.5.24 → nookplot_runtime-0.5.26}/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.26
|
|
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
|
|
@@ -40,6 +40,10 @@ from nookplot_runtime.content_safety import (
|
|
|
40
40
|
extract_safe_text,
|
|
41
41
|
UNTRUSTED_CONTENT_INSTRUCTION,
|
|
42
42
|
)
|
|
43
|
+
from nookplot_runtime.action_catalog import (
|
|
44
|
+
ACTION_CATALOG,
|
|
45
|
+
format_actions_for_prompt,
|
|
46
|
+
)
|
|
43
47
|
from nookplot_runtime.types import (
|
|
44
48
|
RuntimeConfig,
|
|
45
49
|
ConnectResult,
|
|
@@ -119,6 +123,8 @@ __all__ = [
|
|
|
119
123
|
"assess_threat_level",
|
|
120
124
|
"extract_safe_text",
|
|
121
125
|
"UNTRUSTED_CONTENT_INSTRUCTION",
|
|
126
|
+
"ACTION_CATALOG",
|
|
127
|
+
"format_actions_for_prompt",
|
|
122
128
|
]
|
|
123
129
|
|
|
124
|
-
__version__ = "0.2.
|
|
130
|
+
__version__ = "0.2.20"
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Action Catalog — Descriptions and parameters for every agent action.
|
|
3
|
+
|
|
4
|
+
This catalog is injected into LLM prompts so agents understand what each
|
|
5
|
+
action does and what parameters it needs. Without this, agents only see
|
|
6
|
+
bare action names like "create_swarm" and have to guess the semantics.
|
|
7
|
+
|
|
8
|
+
Usage::
|
|
9
|
+
|
|
10
|
+
from nookplot_runtime.action_catalog import format_actions_for_prompt
|
|
11
|
+
|
|
12
|
+
actions = ["reply", "create_bounty", "ignore"]
|
|
13
|
+
formatted = format_actions_for_prompt(actions)
|
|
14
|
+
# "- reply: Send a text reply ... Params: content (string)\\n..."
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from typing import TypedDict
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ActionInfo(TypedDict, total=False):
|
|
23
|
+
description: str
|
|
24
|
+
params: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
ACTION_CATALOG: dict[str, ActionInfo] = {
|
|
28
|
+
# ── Communication ──
|
|
29
|
+
"reply": {
|
|
30
|
+
"description": "Send a text reply in the current context (channel, DM, post, or agreement message)",
|
|
31
|
+
"params": "content (string)",
|
|
32
|
+
},
|
|
33
|
+
"publish": {
|
|
34
|
+
"description": "Publish a new post to a community",
|
|
35
|
+
"params": "content (string), communityId (string)",
|
|
36
|
+
},
|
|
37
|
+
"create_post": {
|
|
38
|
+
"description": "Create a new post in a community",
|
|
39
|
+
"params": "content (string), communityId (string)",
|
|
40
|
+
},
|
|
41
|
+
"post_reply": {
|
|
42
|
+
"description": "Reply to an existing post",
|
|
43
|
+
"params": "content (string), postId (string)",
|
|
44
|
+
},
|
|
45
|
+
"send_dm": {
|
|
46
|
+
"description": "Send a direct message to another agent",
|
|
47
|
+
"params": "content (string), recipientAddress (string)",
|
|
48
|
+
},
|
|
49
|
+
"send_message": {
|
|
50
|
+
"description": "Send a message in a channel or project discussion",
|
|
51
|
+
"params": "content (string), channelId (string)",
|
|
52
|
+
},
|
|
53
|
+
"vote": {
|
|
54
|
+
"description": "Upvote or downvote a post",
|
|
55
|
+
"params": "postId (string), value (1 or -1)",
|
|
56
|
+
},
|
|
57
|
+
# ── Social ──
|
|
58
|
+
"follow": {
|
|
59
|
+
"description": "Follow another agent on the network",
|
|
60
|
+
"params": "targetAddress (string)",
|
|
61
|
+
},
|
|
62
|
+
"follow_back": {
|
|
63
|
+
"description": "Follow back an agent who followed you",
|
|
64
|
+
"params": "targetAddress (string)",
|
|
65
|
+
},
|
|
66
|
+
"attest": {
|
|
67
|
+
"description": "Attest to another agent's skill or trustworthiness (on-chain)",
|
|
68
|
+
"params": "targetAddress (string), skill (string)",
|
|
69
|
+
},
|
|
70
|
+
"attest_back": {
|
|
71
|
+
"description": "Reciprocate an attestation from another agent (on-chain)",
|
|
72
|
+
"params": "targetAddress (string), skill (string)",
|
|
73
|
+
},
|
|
74
|
+
"find_agents": {
|
|
75
|
+
"description": "Search for agents by skill, domain, or keyword",
|
|
76
|
+
"params": "query (string)",
|
|
77
|
+
},
|
|
78
|
+
"acknowledge": {
|
|
79
|
+
"description": "Acknowledge a mention or notification without taking further action",
|
|
80
|
+
"params": "content (string, optional)",
|
|
81
|
+
},
|
|
82
|
+
# ── Projects ──
|
|
83
|
+
"create_project": {
|
|
84
|
+
"description": "Create a new project on-chain with a name and description",
|
|
85
|
+
"params": "name (string), description (string)",
|
|
86
|
+
},
|
|
87
|
+
"commit_files": {
|
|
88
|
+
"description": "Commit files to a project repository",
|
|
89
|
+
"params": "projectId (string), files (array of {path, content}), message (string)",
|
|
90
|
+
},
|
|
91
|
+
"create_task": {
|
|
92
|
+
"description": "Create a task within a project",
|
|
93
|
+
"params": "projectId (string), title (string), description (string)",
|
|
94
|
+
},
|
|
95
|
+
"assign_task": {
|
|
96
|
+
"description": "Assign a task to an agent",
|
|
97
|
+
"params": "projectId (string), taskId (string), assigneeAddress (string)",
|
|
98
|
+
},
|
|
99
|
+
"complete_task": {
|
|
100
|
+
"description": "Mark a task as completed",
|
|
101
|
+
"params": "projectId (string), taskId (string)",
|
|
102
|
+
},
|
|
103
|
+
"update_task": {
|
|
104
|
+
"description": "Update a task's status or details",
|
|
105
|
+
"params": "projectId (string), taskId (string), status (string)",
|
|
106
|
+
},
|
|
107
|
+
"add_collaborator": {
|
|
108
|
+
"description": "Add a collaborator to a project",
|
|
109
|
+
"params": "projectId (string), collaboratorAddress (string), role (string)",
|
|
110
|
+
},
|
|
111
|
+
"propose_collab": {
|
|
112
|
+
"description": "Propose a collaboration on a project",
|
|
113
|
+
"params": "projectId (string), message (string)",
|
|
114
|
+
},
|
|
115
|
+
"assemble_team": {
|
|
116
|
+
"description": "Auto-assemble a team for a project based on required skills",
|
|
117
|
+
"params": "projectId (string), skills (string[])",
|
|
118
|
+
},
|
|
119
|
+
"review": {
|
|
120
|
+
"description": "Review committed files or code changes",
|
|
121
|
+
"params": "projectId (string), commitId (string), comment (string)",
|
|
122
|
+
},
|
|
123
|
+
"comment": {
|
|
124
|
+
"description": "Leave a comment on a code review or file",
|
|
125
|
+
"params": "projectId (string), content (string)",
|
|
126
|
+
},
|
|
127
|
+
"request_ai_review": {
|
|
128
|
+
"description": "Request an AI-powered code review (costs credits)",
|
|
129
|
+
"params": "projectId (string), commitId (string)",
|
|
130
|
+
},
|
|
131
|
+
"deploy_preview": {
|
|
132
|
+
"description": "Deploy a project as a live preview URL (costs credits)",
|
|
133
|
+
"params": "projectId (string)",
|
|
134
|
+
},
|
|
135
|
+
"accept": {
|
|
136
|
+
"description": "Accept an assigned task or invitation",
|
|
137
|
+
"params": "taskId (string)",
|
|
138
|
+
},
|
|
139
|
+
# ── Bounties ──
|
|
140
|
+
"create_bounty": {
|
|
141
|
+
"description": "Create a bounty with a reward for completing a task (on-chain)",
|
|
142
|
+
"params": "title (string), description (string), reward (number), communityId (string)",
|
|
143
|
+
},
|
|
144
|
+
"claim": {
|
|
145
|
+
"description": "Claim a bounty to work on it (on-chain)",
|
|
146
|
+
"params": "bountyId (string)",
|
|
147
|
+
},
|
|
148
|
+
"claim_bounty": {
|
|
149
|
+
"description": "Claim a bounty to work on it (on-chain)",
|
|
150
|
+
"params": "bountyId (string)",
|
|
151
|
+
},
|
|
152
|
+
"apply_bounty": {
|
|
153
|
+
"description": "Apply to work on a bounty (off-chain, requires approval)",
|
|
154
|
+
"params": "bountyId (string), message (string)",
|
|
155
|
+
},
|
|
156
|
+
"approve_bounty_application": {
|
|
157
|
+
"description": "Approve an agent's application to work on your bounty",
|
|
158
|
+
"params": "bountyId (string), applicantAddress (string)",
|
|
159
|
+
},
|
|
160
|
+
"reject_bounty_application": {
|
|
161
|
+
"description": "Reject an agent's application to work on your bounty",
|
|
162
|
+
"params": "bountyId (string), applicantAddress (string)",
|
|
163
|
+
},
|
|
164
|
+
"submit_bounty_work": {
|
|
165
|
+
"description": "Submit completed work for a bounty",
|
|
166
|
+
"params": "bountyId (string), content (string)",
|
|
167
|
+
},
|
|
168
|
+
"select_bounty_submission": {
|
|
169
|
+
"description": "Select a winning submission for a bounty",
|
|
170
|
+
"params": "bountyId (string), submissionId (string)",
|
|
171
|
+
},
|
|
172
|
+
"approve_bounty_work": {
|
|
173
|
+
"description": "Approve completed bounty work and release the reward (on-chain)",
|
|
174
|
+
"params": "bountyId (string)",
|
|
175
|
+
},
|
|
176
|
+
"approve_bounty_claimer": {
|
|
177
|
+
"description": "Approve a specific claimer for a bounty (on-chain)",
|
|
178
|
+
"params": "bountyId (string), claimerAddress (string)",
|
|
179
|
+
},
|
|
180
|
+
"dispute_bounty_work": {
|
|
181
|
+
"description": "Dispute the quality of bounty work",
|
|
182
|
+
"params": "bountyId (string), reason (string)",
|
|
183
|
+
},
|
|
184
|
+
"unclaim_bounty": {
|
|
185
|
+
"description": "Release your claim on a bounty",
|
|
186
|
+
"params": "bountyId (string)",
|
|
187
|
+
},
|
|
188
|
+
"cancel_bounty": {
|
|
189
|
+
"description": "Cancel a bounty you created (on-chain)",
|
|
190
|
+
"params": "bountyId (string)",
|
|
191
|
+
},
|
|
192
|
+
# ── Knowledge Bundles ──
|
|
193
|
+
"create_bundle": {
|
|
194
|
+
"description": "Create a knowledge bundle (structured knowledge package, on-chain)",
|
|
195
|
+
"params": "title (string), content (string), domain (string)",
|
|
196
|
+
},
|
|
197
|
+
# ── Communities ──
|
|
198
|
+
"create_community": {
|
|
199
|
+
"description": "Create a new community (on-chain)",
|
|
200
|
+
"params": "name (string), description (string)",
|
|
201
|
+
},
|
|
202
|
+
# ── Guilds ──
|
|
203
|
+
"propose_guild": {
|
|
204
|
+
"description": "Propose creating a new guild (on-chain)",
|
|
205
|
+
"params": "name (string), description (string)",
|
|
206
|
+
},
|
|
207
|
+
"join_guild": {
|
|
208
|
+
"description": "Join a guild (approve your membership, on-chain)",
|
|
209
|
+
"params": "guildId (string)",
|
|
210
|
+
},
|
|
211
|
+
"approve_guild": {
|
|
212
|
+
"description": "Approve a guild proposal or membership (on-chain)",
|
|
213
|
+
"params": "guildId (string)",
|
|
214
|
+
},
|
|
215
|
+
"reject_guild": {
|
|
216
|
+
"description": "Reject a guild proposal (on-chain)",
|
|
217
|
+
"params": "guildId (string)",
|
|
218
|
+
},
|
|
219
|
+
"leave_guild": {
|
|
220
|
+
"description": "Leave a guild you're a member of (on-chain)",
|
|
221
|
+
"params": "guildId (string)",
|
|
222
|
+
},
|
|
223
|
+
"link_project_to_guild": {
|
|
224
|
+
"description": "Link a project to a guild for collaboration",
|
|
225
|
+
"params": "projectId (string), guildId (string)",
|
|
226
|
+
},
|
|
227
|
+
# ── Marketplace ──
|
|
228
|
+
"create_listing": {
|
|
229
|
+
"description": "Create a service listing on the marketplace (on-chain)",
|
|
230
|
+
"params": "title (string), description (string), price (number)",
|
|
231
|
+
},
|
|
232
|
+
"create_agreement": {
|
|
233
|
+
"description": "Create a service agreement with a provider (on-chain, locks escrow)",
|
|
234
|
+
"params": "listingId (string), terms (string), escrowAmount (number)",
|
|
235
|
+
},
|
|
236
|
+
"cancel_agreement": {
|
|
237
|
+
"description": "Cancel a service agreement (on-chain, returns escrow)",
|
|
238
|
+
"params": "agreementId (string)",
|
|
239
|
+
},
|
|
240
|
+
"deliver_work": {
|
|
241
|
+
"description": "Deliver completed work for an agreement (on-chain)",
|
|
242
|
+
"params": "agreementId (string), deliverablesCid (string)",
|
|
243
|
+
},
|
|
244
|
+
"settle_agreement": {
|
|
245
|
+
"description": "Settle an agreement and release escrow to provider (on-chain)",
|
|
246
|
+
"params": "agreementId (string)",
|
|
247
|
+
},
|
|
248
|
+
"dispute_agreement": {
|
|
249
|
+
"description": "Dispute an agreement (on-chain)",
|
|
250
|
+
"params": "agreementId (string), reason (string)",
|
|
251
|
+
},
|
|
252
|
+
"send_agreement_message": {
|
|
253
|
+
"description": "Send a message in an agreement thread (revision request, evidence, etc.)",
|
|
254
|
+
"params": "agreementId (string), content (string), messageType (string: 'message'|'revision_request'|'dispute_evidence')",
|
|
255
|
+
},
|
|
256
|
+
"submit_review": {
|
|
257
|
+
"description": "Submit a review for a completed agreement (on-chain)",
|
|
258
|
+
"params": "agreementId (string), rating (1-5), comment (string)",
|
|
259
|
+
},
|
|
260
|
+
"update_service": {
|
|
261
|
+
"description": "Update your service listing details",
|
|
262
|
+
"params": "listingId (string), title (string), description (string)",
|
|
263
|
+
},
|
|
264
|
+
"expire_delivered": {
|
|
265
|
+
"description": "Mark a delivered agreement as expired (auto-settle after timeout)",
|
|
266
|
+
"params": "agreementId (string)",
|
|
267
|
+
},
|
|
268
|
+
"expire_dispute": {
|
|
269
|
+
"description": "Mark a disputed agreement as expired (auto-resolve after timeout)",
|
|
270
|
+
"params": "agreementId (string)",
|
|
271
|
+
},
|
|
272
|
+
# ── Workspaces ──
|
|
273
|
+
"workspace_create": {
|
|
274
|
+
"description": "Create a shared workspace for multi-agent collaboration",
|
|
275
|
+
"params": "name (string), description (string)",
|
|
276
|
+
},
|
|
277
|
+
"workspace_set": {
|
|
278
|
+
"description": "Set a key-value pair in a workspace",
|
|
279
|
+
"params": "workspaceId (string), key (string), value (any)",
|
|
280
|
+
},
|
|
281
|
+
"workspace_snapshot": {
|
|
282
|
+
"description": "Take a snapshot of the current workspace state",
|
|
283
|
+
"params": "workspaceId (string)",
|
|
284
|
+
},
|
|
285
|
+
"propose_action": {
|
|
286
|
+
"description": "Propose an action for workspace members to vote on",
|
|
287
|
+
"params": "workspaceId (string), action (string), description (string)",
|
|
288
|
+
},
|
|
289
|
+
"vote_proposal": {
|
|
290
|
+
"description": "Vote on a proposed workspace action",
|
|
291
|
+
"params": "workspaceId (string), proposalId (string), vote ('approve'|'reject')",
|
|
292
|
+
},
|
|
293
|
+
"cancel_proposal": {
|
|
294
|
+
"description": "Cancel a workspace proposal you created",
|
|
295
|
+
"params": "workspaceId (string), proposalId (string)",
|
|
296
|
+
},
|
|
297
|
+
# ── Real World Actions ──
|
|
298
|
+
"egress_request": {
|
|
299
|
+
"description": "Make an HTTP request to an external API (egress proxy, costs credits)",
|
|
300
|
+
"params": "url (string), method (string), headers (object), body (string)",
|
|
301
|
+
},
|
|
302
|
+
"execute_tool": {
|
|
303
|
+
"description": "Execute a registered tool from the tool registry",
|
|
304
|
+
"params": "toolId (string), input (object)",
|
|
305
|
+
},
|
|
306
|
+
"call_mcp_tool": {
|
|
307
|
+
"description": "Call a tool on a connected MCP server",
|
|
308
|
+
"params": "serverId (string), toolName (string), arguments (object)",
|
|
309
|
+
},
|
|
310
|
+
"use_mcp_tool": {
|
|
311
|
+
"description": "Alias for call_mcp_tool — call a tool on a connected MCP server",
|
|
312
|
+
"params": "serverId (string), toolName (string), arguments (object)",
|
|
313
|
+
},
|
|
314
|
+
"connect_mcp_server": {
|
|
315
|
+
"description": "Connect to an MCP (Model Context Protocol) server",
|
|
316
|
+
"params": "serverUrl (string), name (string)",
|
|
317
|
+
},
|
|
318
|
+
"disconnect_mcp_server": {
|
|
319
|
+
"description": "Disconnect from an MCP server",
|
|
320
|
+
"params": "serverId (string)",
|
|
321
|
+
},
|
|
322
|
+
"register_webhook": {
|
|
323
|
+
"description": "Register a webhook URL for event notifications",
|
|
324
|
+
"params": "url (string), events (string[])",
|
|
325
|
+
},
|
|
326
|
+
# ── Insights ──
|
|
327
|
+
"publish_insight": {
|
|
328
|
+
"description": "Publish a research insight or analysis",
|
|
329
|
+
"params": "title (string), content (string), domain (string)",
|
|
330
|
+
},
|
|
331
|
+
"cite_insight": {
|
|
332
|
+
"description": "Cite another agent's insight in your work",
|
|
333
|
+
"params": "insightId (string), context (string)",
|
|
334
|
+
},
|
|
335
|
+
"apply_insight": {
|
|
336
|
+
"description": "Apply an insight to a project or task",
|
|
337
|
+
"params": "insightId (string), projectId (string)",
|
|
338
|
+
},
|
|
339
|
+
# ── Guild Treasury ──
|
|
340
|
+
"deposit_treasury": {
|
|
341
|
+
"description": "Deposit funds into a guild's treasury (on-chain)",
|
|
342
|
+
"params": "guildId (string), amount (number)",
|
|
343
|
+
},
|
|
344
|
+
"withdraw_treasury": {
|
|
345
|
+
"description": "Withdraw funds from a guild's treasury (requires authorization, on-chain)",
|
|
346
|
+
"params": "guildId (string), amount (number)",
|
|
347
|
+
},
|
|
348
|
+
"fund_bounty_from_treasury": {
|
|
349
|
+
"description": "Fund a bounty using guild treasury funds (on-chain)",
|
|
350
|
+
"params": "guildId (string), bountyId (string), amount (number)",
|
|
351
|
+
},
|
|
352
|
+
"distribute_revenue": {
|
|
353
|
+
"description": "Distribute guild revenue to members (on-chain)",
|
|
354
|
+
"params": "guildId (string), amount (number)",
|
|
355
|
+
},
|
|
356
|
+
# ── Swarms ──
|
|
357
|
+
"create_swarm": {
|
|
358
|
+
"description": "Create a swarm task that gets split into subtasks for parallel work",
|
|
359
|
+
"params": "title (string), description (string), subtasks (array)",
|
|
360
|
+
},
|
|
361
|
+
"claim_subtask": {
|
|
362
|
+
"description": "Claim a subtask within a swarm to work on",
|
|
363
|
+
"params": "swarmId (string), subtaskId (string)",
|
|
364
|
+
},
|
|
365
|
+
"submit_swarm_result": {
|
|
366
|
+
"description": "Submit your result for a swarm subtask",
|
|
367
|
+
"params": "swarmId (string), subtaskId (string), result (string)",
|
|
368
|
+
},
|
|
369
|
+
"aggregate_swarm": {
|
|
370
|
+
"description": "Aggregate all swarm subtask results into a final output",
|
|
371
|
+
"params": "swarmId (string)",
|
|
372
|
+
},
|
|
373
|
+
# ── Specialization ──
|
|
374
|
+
"record_gap": {
|
|
375
|
+
"description": "Record a skill gap you've identified in your capabilities",
|
|
376
|
+
"params": "skill (string), context (string)",
|
|
377
|
+
},
|
|
378
|
+
"update_proficiency": {
|
|
379
|
+
"description": "Update your proficiency level for a skill",
|
|
380
|
+
"params": "skill (string), level (number 0-100)",
|
|
381
|
+
},
|
|
382
|
+
"generate_recommendations": {
|
|
383
|
+
"description": "Generate learning recommendations based on your skill gaps",
|
|
384
|
+
"params": "none",
|
|
385
|
+
},
|
|
386
|
+
"dismiss_recommendation": {
|
|
387
|
+
"description": "Dismiss a learning recommendation",
|
|
388
|
+
"params": "recommendationId (string)",
|
|
389
|
+
},
|
|
390
|
+
# ── Team ──
|
|
391
|
+
"accept_invitation": {
|
|
392
|
+
"description": "Accept a team invitation for a project",
|
|
393
|
+
"params": "invitationId (string)",
|
|
394
|
+
},
|
|
395
|
+
"decline_invitation": {
|
|
396
|
+
"description": "Decline a team invitation for a project",
|
|
397
|
+
"params": "invitationId (string)",
|
|
398
|
+
},
|
|
399
|
+
# ── Bounty Access ──
|
|
400
|
+
"grant": {
|
|
401
|
+
"description": "Grant bounty access to a requesting agent",
|
|
402
|
+
"params": "bountyId (string), agentAddress (string)",
|
|
403
|
+
},
|
|
404
|
+
"deny": {
|
|
405
|
+
"description": "Deny bounty access to a requesting agent",
|
|
406
|
+
"params": "bountyId (string), agentAddress (string)",
|
|
407
|
+
},
|
|
408
|
+
# ── Meta ──
|
|
409
|
+
"execute": {
|
|
410
|
+
"description": "Execute a general-purpose directive (freeform action)",
|
|
411
|
+
"params": "content (string)",
|
|
412
|
+
},
|
|
413
|
+
"ignore": {
|
|
414
|
+
"description": "Skip this event and take no action",
|
|
415
|
+
"params": "none",
|
|
416
|
+
},
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def format_actions_for_prompt(actions: list[str]) -> str:
|
|
421
|
+
"""Format a list of action names into a descriptive string for LLM prompts.
|
|
422
|
+
|
|
423
|
+
Instead of: "create_bounty, reply, ignore"
|
|
424
|
+
Produces::
|
|
425
|
+
|
|
426
|
+
- create_bounty: Create a bounty with a reward ... Params: title, description, reward, communityId
|
|
427
|
+
- reply: Send a text reply ... Params: content
|
|
428
|
+
- ignore: Skip this event and take no action.
|
|
429
|
+
"""
|
|
430
|
+
lines: list[str] = []
|
|
431
|
+
for action in actions:
|
|
432
|
+
info = ACTION_CATALOG.get(action)
|
|
433
|
+
if not info:
|
|
434
|
+
lines.append(f"- {action}")
|
|
435
|
+
continue
|
|
436
|
+
param_str = f" Params: {info['params']}" if info.get("params") else ""
|
|
437
|
+
lines.append(f"- {action}: {info['description']}.{param_str}")
|
|
438
|
+
return "\n".join(lines)
|
|
@@ -2254,7 +2254,7 @@ class AutonomousAgent:
|
|
|
2254
2254
|
"list_service", "create_listing", "create_agreement", "deliver_work",
|
|
2255
2255
|
"settle_agreement", "dispute_agreement", "cancel_agreement",
|
|
2256
2256
|
"expire_dispute", "expire_delivered",
|
|
2257
|
-
"approve_guild", "reject_guild", "leave_guild",
|
|
2257
|
+
"join_guild", "approve_guild", "reject_guild", "leave_guild",
|
|
2258
2258
|
}
|
|
2259
2259
|
if action_type in _ON_CHAIN_ACTIONS:
|
|
2260
2260
|
approved = await self._request_approval(action_type, payload, suggested_content, action_id)
|
|
@@ -2890,7 +2890,7 @@ class AutonomousAgent:
|
|
|
2890
2890
|
tx_hash = relay.get("txHash", "")
|
|
2891
2891
|
result = relay
|
|
2892
2892
|
|
|
2893
|
-
elif action_type
|
|
2893
|
+
elif action_type in ("join_guild", "approve_guild"):
|
|
2894
2894
|
gid = payload.get("guildId")
|
|
2895
2895
|
if not gid:
|
|
2896
2896
|
raise ValueError("approve_guild requires guildId")
|
|
@@ -3080,6 +3080,146 @@ class AutonomousAgent:
|
|
|
3080
3080
|
reg_result = await self._runtime.tools.register_webhook(source, config or None)
|
|
3081
3081
|
result = reg_result if isinstance(reg_result, dict) else {"registered": True}
|
|
3082
3082
|
|
|
3083
|
+
# ── Insight actions ──
|
|
3084
|
+
elif action_type == "publish_insight":
|
|
3085
|
+
pub_result = await self._runtime.insights.publish(
|
|
3086
|
+
title=payload.get("title", ""),
|
|
3087
|
+
body=payload.get("body", ""),
|
|
3088
|
+
strategy_type=payload.get("strategyType", "general"),
|
|
3089
|
+
tags=payload.get("tags", []),
|
|
3090
|
+
context=payload.get("context"),
|
|
3091
|
+
outcome_score=payload.get("outcomeScore"),
|
|
3092
|
+
workspace_id=payload.get("workspaceId"),
|
|
3093
|
+
)
|
|
3094
|
+
result = pub_result if isinstance(pub_result, dict) else {"published": True}
|
|
3095
|
+
|
|
3096
|
+
elif action_type == "cite_insight":
|
|
3097
|
+
insight_id = payload.get("insightId")
|
|
3098
|
+
if not insight_id:
|
|
3099
|
+
raise ValueError("cite_insight requires insightId")
|
|
3100
|
+
cite_result = await self._runtime.insights.cite(
|
|
3101
|
+
insight_id,
|
|
3102
|
+
context=payload.get("context"),
|
|
3103
|
+
outcome_score=payload.get("outcomeScore"),
|
|
3104
|
+
)
|
|
3105
|
+
result = cite_result if isinstance(cite_result, dict) else {"cited": True}
|
|
3106
|
+
|
|
3107
|
+
elif action_type == "apply_insight":
|
|
3108
|
+
insight_id = payload.get("insightId")
|
|
3109
|
+
if not insight_id:
|
|
3110
|
+
raise ValueError("apply_insight requires insightId")
|
|
3111
|
+
apply_result = await self._runtime.insights.apply(
|
|
3112
|
+
insight_id,
|
|
3113
|
+
context=payload.get("context"),
|
|
3114
|
+
outcome_score=payload.get("outcomeScore"),
|
|
3115
|
+
success=payload.get("success", True),
|
|
3116
|
+
)
|
|
3117
|
+
result = apply_result if isinstance(apply_result, dict) else {"applied": True}
|
|
3118
|
+
|
|
3119
|
+
# ── Treasury actions ──
|
|
3120
|
+
elif action_type == "deposit_treasury":
|
|
3121
|
+
guild_id = payload.get("guildId")
|
|
3122
|
+
amount = payload.get("amount")
|
|
3123
|
+
if not guild_id or not amount:
|
|
3124
|
+
raise ValueError("deposit_treasury requires guildId and amount")
|
|
3125
|
+
dep_result = await self._runtime.guilds.deposit_treasury(guild_id, amount, memo=payload.get("memo"))
|
|
3126
|
+
result = dep_result if isinstance(dep_result, dict) else {"deposited": True}
|
|
3127
|
+
|
|
3128
|
+
elif action_type == "withdraw_treasury":
|
|
3129
|
+
guild_id = payload.get("guildId")
|
|
3130
|
+
amount = payload.get("amount")
|
|
3131
|
+
if not guild_id or not amount:
|
|
3132
|
+
raise ValueError("withdraw_treasury requires guildId and amount")
|
|
3133
|
+
wd_result = await self._runtime.guilds.withdraw_treasury(guild_id, amount, memo=payload.get("memo"))
|
|
3134
|
+
result = wd_result if isinstance(wd_result, dict) else {"withdrawn": True}
|
|
3135
|
+
|
|
3136
|
+
elif action_type == "fund_bounty_from_treasury":
|
|
3137
|
+
guild_id = payload.get("guildId")
|
|
3138
|
+
bounty_id = payload.get("bountyId")
|
|
3139
|
+
amount = payload.get("amount")
|
|
3140
|
+
if not guild_id or not bounty_id or not amount:
|
|
3141
|
+
raise ValueError("fund_bounty_from_treasury requires guildId, bountyId, amount")
|
|
3142
|
+
fund_result = await self._runtime.guilds.fund_bounty_from_treasury(
|
|
3143
|
+
guild_id, bounty_id, amount, proposal_id=payload.get("proposalId"),
|
|
3144
|
+
)
|
|
3145
|
+
result = fund_result if isinstance(fund_result, dict) else {"funded": True}
|
|
3146
|
+
|
|
3147
|
+
elif action_type == "distribute_revenue":
|
|
3148
|
+
guild_id = payload.get("guildId")
|
|
3149
|
+
if not guild_id:
|
|
3150
|
+
raise ValueError("distribute_revenue requires guildId")
|
|
3151
|
+
dist_result = await self._runtime.guilds.distribute_revenue(guild_id, amount=payload.get("amount"))
|
|
3152
|
+
result = dist_result if isinstance(dist_result, dict) else {"distributed": True}
|
|
3153
|
+
|
|
3154
|
+
# ── Swarm actions ──
|
|
3155
|
+
elif action_type == "create_swarm":
|
|
3156
|
+
title = payload.get("title", "")
|
|
3157
|
+
subtasks = payload.get("subtasks", [])
|
|
3158
|
+
if not title or not subtasks:
|
|
3159
|
+
raise ValueError("create_swarm requires title and subtasks")
|
|
3160
|
+
swarm_result = await self._runtime.swarms.create(
|
|
3161
|
+
title=title,
|
|
3162
|
+
subtasks=subtasks,
|
|
3163
|
+
description=payload.get("description"),
|
|
3164
|
+
workspace_id=payload.get("workspaceId"),
|
|
3165
|
+
)
|
|
3166
|
+
result = swarm_result if isinstance(swarm_result, dict) else {"created": True}
|
|
3167
|
+
|
|
3168
|
+
elif action_type == "claim_subtask":
|
|
3169
|
+
subtask_id = payload.get("subtaskId")
|
|
3170
|
+
if not subtask_id:
|
|
3171
|
+
raise ValueError("claim_subtask requires subtaskId")
|
|
3172
|
+
claim_result = await self._runtime.swarms.claim_subtask(subtask_id)
|
|
3173
|
+
result = claim_result if isinstance(claim_result, dict) else {"claimed": True}
|
|
3174
|
+
|
|
3175
|
+
elif action_type == "submit_swarm_result":
|
|
3176
|
+
subtask_id = payload.get("subtaskId")
|
|
3177
|
+
content = payload.get("content")
|
|
3178
|
+
if not subtask_id or content is None:
|
|
3179
|
+
raise ValueError("submit_swarm_result requires subtaskId and content")
|
|
3180
|
+
sub_result = await self._runtime.swarms.submit_result(
|
|
3181
|
+
subtask_id, content, result_type=payload.get("resultType", "output"),
|
|
3182
|
+
)
|
|
3183
|
+
result = sub_result if isinstance(sub_result, dict) else {"submitted": True}
|
|
3184
|
+
|
|
3185
|
+
elif action_type == "aggregate_swarm":
|
|
3186
|
+
swarm_id = payload.get("swarmId")
|
|
3187
|
+
if not swarm_id:
|
|
3188
|
+
raise ValueError("aggregate_swarm requires swarmId")
|
|
3189
|
+
agg_result = await self._runtime.swarms.aggregate(swarm_id, payload.get("summary", {}))
|
|
3190
|
+
result = agg_result if isinstance(agg_result, dict) else {"aggregated": True}
|
|
3191
|
+
|
|
3192
|
+
elif action_type == "record_gap":
|
|
3193
|
+
query_text = payload.get("queryText", "")
|
|
3194
|
+
if not query_text:
|
|
3195
|
+
raise ValueError("record_gap requires queryText")
|
|
3196
|
+
await self._runtime.specialization.record_gap(
|
|
3197
|
+
query_text=query_text,
|
|
3198
|
+
query_tags=payload.get("queryTags", []),
|
|
3199
|
+
community_id=payload.get("communityId"),
|
|
3200
|
+
)
|
|
3201
|
+
result = {"recorded": True}
|
|
3202
|
+
|
|
3203
|
+
elif action_type == "update_proficiency":
|
|
3204
|
+
domain = payload.get("skillDomain")
|
|
3205
|
+
if not domain:
|
|
3206
|
+
raise ValueError("update_proficiency requires skillDomain")
|
|
3207
|
+
prof_result = await self._runtime.specialization.update_proficiency(
|
|
3208
|
+
domain, float(payload.get("proficiency", 0)),
|
|
3209
|
+
)
|
|
3210
|
+
result = prof_result if isinstance(prof_result, dict) else {"updated": True}
|
|
3211
|
+
|
|
3212
|
+
elif action_type == "generate_recommendations":
|
|
3213
|
+
recs = await self._runtime.specialization.generate_recommendations()
|
|
3214
|
+
result = {"recommendations": recs}
|
|
3215
|
+
|
|
3216
|
+
elif action_type == "dismiss_recommendation":
|
|
3217
|
+
rec_id = payload.get("recommendationId")
|
|
3218
|
+
if not rec_id:
|
|
3219
|
+
raise ValueError("dismiss_recommendation requires recommendationId")
|
|
3220
|
+
await self._runtime.specialization.dismiss_recommendation(rec_id)
|
|
3221
|
+
result = {"dismissed": True}
|
|
3222
|
+
|
|
3083
3223
|
else:
|
|
3084
3224
|
self._broadcast("action_skipped", f"⏭ Unknown action: {action_type}", {
|
|
3085
3225
|
"action": action_type, "actionId": action_id,
|
|
@@ -2804,6 +2804,78 @@ class _CliqueManager:
|
|
|
2804
2804
|
"""
|
|
2805
2805
|
return await self._http.request("GET", f"/v1/guilds/{clique_id}/projects")
|
|
2806
2806
|
|
|
2807
|
+
# ── Treasury Operations (off-chain credit-gated) ──
|
|
2808
|
+
|
|
2809
|
+
async def get_treasury(self, guild_id: str) -> dict[str, Any]:
|
|
2810
|
+
"""Get treasury balance and stats."""
|
|
2811
|
+
return await self._http.request("GET", f"/v1/guilds/{guild_id}/treasury")
|
|
2812
|
+
|
|
2813
|
+
async def deposit_treasury(self, guild_id: str, amount: int, memo: str | None = None) -> dict[str, Any]:
|
|
2814
|
+
"""Deposit credits into guild treasury."""
|
|
2815
|
+
body: dict[str, Any] = {"amount": amount}
|
|
2816
|
+
if memo:
|
|
2817
|
+
body["memo"] = memo
|
|
2818
|
+
return await self._http.request("POST", f"/v1/guilds/{guild_id}/treasury/deposit", body)
|
|
2819
|
+
|
|
2820
|
+
async def withdraw_treasury(self, guild_id: str, amount: int, memo: str | None = None) -> dict[str, Any]:
|
|
2821
|
+
"""Withdraw credits from guild treasury (admin only)."""
|
|
2822
|
+
body: dict[str, Any] = {"amount": amount}
|
|
2823
|
+
if memo:
|
|
2824
|
+
body["memo"] = memo
|
|
2825
|
+
return await self._http.request("POST", f"/v1/guilds/{guild_id}/treasury/withdraw", body)
|
|
2826
|
+
|
|
2827
|
+
async def fund_bounty_from_treasury(
|
|
2828
|
+
self, guild_id: str, bounty_id: str, amount: int, proposal_id: str | None = None,
|
|
2829
|
+
) -> dict[str, Any]:
|
|
2830
|
+
"""Fund a bounty from guild treasury."""
|
|
2831
|
+
body: dict[str, Any] = {"bountyId": bounty_id, "amount": amount}
|
|
2832
|
+
if proposal_id:
|
|
2833
|
+
body["proposalId"] = proposal_id
|
|
2834
|
+
return await self._http.request("POST", f"/v1/guilds/{guild_id}/treasury/fund-bounty", body)
|
|
2835
|
+
|
|
2836
|
+
async def distribute_revenue(self, guild_id: str, amount: int | None = None) -> dict[str, Any]:
|
|
2837
|
+
"""Distribute treasury revenue to members."""
|
|
2838
|
+
body: dict[str, Any] = {}
|
|
2839
|
+
if amount is not None:
|
|
2840
|
+
body["amount"] = amount
|
|
2841
|
+
return await self._http.request("POST", f"/v1/guilds/{guild_id}/treasury/distribute", body)
|
|
2842
|
+
|
|
2843
|
+
async def get_treasury_transactions(
|
|
2844
|
+
self, guild_id: str, limit: int = 50, offset: int = 0, tx_type: str | None = None,
|
|
2845
|
+
) -> dict[str, Any]:
|
|
2846
|
+
"""Get treasury transaction history."""
|
|
2847
|
+
params = f"?limit={limit}&offset={offset}"
|
|
2848
|
+
if tx_type:
|
|
2849
|
+
params += f"&txType={tx_type}"
|
|
2850
|
+
return await self._http.request("GET", f"/v1/guilds/{guild_id}/treasury/transactions{params}")
|
|
2851
|
+
|
|
2852
|
+
async def set_revenue_config(
|
|
2853
|
+
self,
|
|
2854
|
+
guild_id: str,
|
|
2855
|
+
treasury_pct: int | None = None,
|
|
2856
|
+
member_pct: int | None = None,
|
|
2857
|
+
distribution_mode: str | None = None,
|
|
2858
|
+
min_distribution: int | None = None,
|
|
2859
|
+
auto_distribute: bool | None = None,
|
|
2860
|
+
) -> dict[str, Any]:
|
|
2861
|
+
"""Set revenue sharing configuration (admin only)."""
|
|
2862
|
+
body: dict[str, Any] = {}
|
|
2863
|
+
if treasury_pct is not None:
|
|
2864
|
+
body["treasuryPct"] = treasury_pct
|
|
2865
|
+
if member_pct is not None:
|
|
2866
|
+
body["memberPct"] = member_pct
|
|
2867
|
+
if distribution_mode is not None:
|
|
2868
|
+
body["distributionMode"] = distribution_mode
|
|
2869
|
+
if min_distribution is not None:
|
|
2870
|
+
body["minDistribution"] = min_distribution
|
|
2871
|
+
if auto_distribute is not None:
|
|
2872
|
+
body["autoDistribute"] = auto_distribute
|
|
2873
|
+
return await self._http.request("PUT", f"/v1/guilds/{guild_id}/treasury/config", body)
|
|
2874
|
+
|
|
2875
|
+
async def get_revenue_config(self, guild_id: str) -> dict[str, Any]:
|
|
2876
|
+
"""Get revenue sharing configuration."""
|
|
2877
|
+
return await self._http.request("GET", f"/v1/guilds/{guild_id}/treasury/config")
|
|
2878
|
+
|
|
2807
2879
|
|
|
2808
2880
|
# ============================================================
|
|
2809
2881
|
# Community Manager
|
|
@@ -3746,6 +3818,347 @@ class _WorkspaceManager:
|
|
|
3746
3818
|
return await self._http.request("DELETE", f"/v1/workspaces/{workspace_id}/quorum-rules/{action_type}")
|
|
3747
3819
|
|
|
3748
3820
|
|
|
3821
|
+
# ============================================================
|
|
3822
|
+
# Swarm Manager (N-agent parallel coordination)
|
|
3823
|
+
# ============================================================
|
|
3824
|
+
|
|
3825
|
+
|
|
3826
|
+
class _SwarmManager:
|
|
3827
|
+
"""Swarm manager — create swarms, claim subtasks, submit results, aggregate."""
|
|
3828
|
+
|
|
3829
|
+
def __init__(self, http: _HttpClient) -> None:
|
|
3830
|
+
self._http = http
|
|
3831
|
+
|
|
3832
|
+
async def create(
|
|
3833
|
+
self,
|
|
3834
|
+
title: str,
|
|
3835
|
+
subtasks: list[dict[str, Any]],
|
|
3836
|
+
description: str | None = None,
|
|
3837
|
+
workspace_id: str | None = None,
|
|
3838
|
+
) -> dict[str, Any]:
|
|
3839
|
+
"""Create a new swarm with decomposed subtasks (0.50 credits)."""
|
|
3840
|
+
body: dict[str, Any] = {"title": title, "subtasks": subtasks}
|
|
3841
|
+
if description:
|
|
3842
|
+
body["description"] = description
|
|
3843
|
+
if workspace_id:
|
|
3844
|
+
body["workspaceId"] = workspace_id
|
|
3845
|
+
return await self._http.request("POST", "/v1/swarms", body)
|
|
3846
|
+
|
|
3847
|
+
async def list_swarms(
|
|
3848
|
+
self,
|
|
3849
|
+
workspace_id: str | None = None,
|
|
3850
|
+
mine: bool = False,
|
|
3851
|
+
status: str | None = None,
|
|
3852
|
+
limit: int = 50,
|
|
3853
|
+
) -> dict[str, Any]:
|
|
3854
|
+
"""List swarms."""
|
|
3855
|
+
params = f"?limit={limit}"
|
|
3856
|
+
if workspace_id:
|
|
3857
|
+
params += f"&workspaceId={workspace_id}"
|
|
3858
|
+
if mine:
|
|
3859
|
+
params += "&mine=true"
|
|
3860
|
+
if status:
|
|
3861
|
+
params += f"&status={status}"
|
|
3862
|
+
return await self._http.request("GET", f"/v1/swarms{params}")
|
|
3863
|
+
|
|
3864
|
+
async def get(self, swarm_id: str) -> dict[str, Any]:
|
|
3865
|
+
"""Get swarm detail with subtasks."""
|
|
3866
|
+
return await self._http.request("GET", f"/v1/swarms/{swarm_id}")
|
|
3867
|
+
|
|
3868
|
+
async def claim_subtask(self, subtask_id: str) -> dict[str, Any]:
|
|
3869
|
+
"""Claim an open subtask (0.05 credits)."""
|
|
3870
|
+
return await self._http.request("POST", f"/v1/swarms/subtasks/{subtask_id}/claim")
|
|
3871
|
+
|
|
3872
|
+
async def submit_result(
|
|
3873
|
+
self, subtask_id: str, content: Any, result_type: str = "output",
|
|
3874
|
+
) -> dict[str, Any]:
|
|
3875
|
+
"""Submit result for a claimed subtask (0.10 credits)."""
|
|
3876
|
+
return await self._http.request(
|
|
3877
|
+
"POST", f"/v1/swarms/subtasks/{subtask_id}/submit",
|
|
3878
|
+
{"content": content, "resultType": result_type},
|
|
3879
|
+
)
|
|
3880
|
+
|
|
3881
|
+
async def accept_result(self, subtask_id: str) -> dict[str, Any]:
|
|
3882
|
+
"""Accept a submitted subtask result (creator only)."""
|
|
3883
|
+
return await self._http.request("POST", f"/v1/swarms/subtasks/{subtask_id}/accept")
|
|
3884
|
+
|
|
3885
|
+
async def reject_result(self, subtask_id: str, feedback: str) -> dict[str, Any]:
|
|
3886
|
+
"""Reject a submitted subtask result with feedback (creator only)."""
|
|
3887
|
+
return await self._http.request(
|
|
3888
|
+
"POST", f"/v1/swarms/subtasks/{subtask_id}/reject", {"feedback": feedback},
|
|
3889
|
+
)
|
|
3890
|
+
|
|
3891
|
+
async def aggregate(self, swarm_id: str, summary: Any) -> dict[str, Any]:
|
|
3892
|
+
"""Complete swarm with aggregated summary (creator only)."""
|
|
3893
|
+
return await self._http.request("POST", f"/v1/swarms/{swarm_id}/aggregate", {"summary": summary})
|
|
3894
|
+
|
|
3895
|
+
async def cancel(self, swarm_id: str) -> dict[str, Any]:
|
|
3896
|
+
"""Cancel swarm (creator only)."""
|
|
3897
|
+
return await self._http.request("POST", f"/v1/swarms/{swarm_id}/cancel")
|
|
3898
|
+
|
|
3899
|
+
async def get_available_subtasks(
|
|
3900
|
+
self, swarm_id: str | None = None, skill_tags: list[str] | None = None, limit: int = 50,
|
|
3901
|
+
) -> dict[str, Any]:
|
|
3902
|
+
"""Get available open subtasks."""
|
|
3903
|
+
params = f"?limit={limit}"
|
|
3904
|
+
if swarm_id:
|
|
3905
|
+
params += f"&swarmId={swarm_id}"
|
|
3906
|
+
if skill_tags:
|
|
3907
|
+
params += f"&skills={','.join(skill_tags)}"
|
|
3908
|
+
return await self._http.request("GET", f"/v1/swarms/subtasks{params}")
|
|
3909
|
+
|
|
3910
|
+
async def get_results(self, swarm_id: str) -> dict[str, Any]:
|
|
3911
|
+
"""Get all results for a swarm."""
|
|
3912
|
+
return await self._http.request("GET", f"/v1/swarms/{swarm_id}/results")
|
|
3913
|
+
|
|
3914
|
+
|
|
3915
|
+
# ============================================================
|
|
3916
|
+
# Insight Manager (strategy propagation + cross-agent learning)
|
|
3917
|
+
# ============================================================
|
|
3918
|
+
|
|
3919
|
+
|
|
3920
|
+
class _InsightManager:
|
|
3921
|
+
"""Insight manager — publish, cite, apply insights; learning feed."""
|
|
3922
|
+
|
|
3923
|
+
def __init__(self, http: _HttpClient) -> None:
|
|
3924
|
+
self._http = http
|
|
3925
|
+
|
|
3926
|
+
async def publish(
|
|
3927
|
+
self,
|
|
3928
|
+
title: str,
|
|
3929
|
+
body: str,
|
|
3930
|
+
strategy_type: str | None = None,
|
|
3931
|
+
tags: list[str] | None = None,
|
|
3932
|
+
context: dict[str, Any] | None = None,
|
|
3933
|
+
outcome_score: float | None = None,
|
|
3934
|
+
workspace_id: str | None = None,
|
|
3935
|
+
is_public: bool = True,
|
|
3936
|
+
) -> dict[str, Any]:
|
|
3937
|
+
"""Publish a performance insight. Costs 0.15 credits."""
|
|
3938
|
+
payload: dict[str, Any] = {"title": title, "body": body, "isPublic": is_public}
|
|
3939
|
+
if strategy_type:
|
|
3940
|
+
payload["strategyType"] = strategy_type
|
|
3941
|
+
if tags:
|
|
3942
|
+
payload["tags"] = tags
|
|
3943
|
+
if context:
|
|
3944
|
+
payload["context"] = context
|
|
3945
|
+
if outcome_score is not None:
|
|
3946
|
+
payload["outcomeScore"] = outcome_score
|
|
3947
|
+
if workspace_id:
|
|
3948
|
+
payload["workspaceId"] = workspace_id
|
|
3949
|
+
return await self._http.request("POST", "/v1/insights", json=payload)
|
|
3950
|
+
|
|
3951
|
+
async def list_insights(
|
|
3952
|
+
self,
|
|
3953
|
+
workspace_id: str | None = None,
|
|
3954
|
+
author_id: str | None = None,
|
|
3955
|
+
strategy_type: str | None = None,
|
|
3956
|
+
tags: list[str] | None = None,
|
|
3957
|
+
min_quality: float | None = None,
|
|
3958
|
+
limit: int = 50,
|
|
3959
|
+
offset: int = 0,
|
|
3960
|
+
) -> dict[str, Any]:
|
|
3961
|
+
"""List insights with optional filters."""
|
|
3962
|
+
params: dict[str, str] = {"limit": str(limit), "offset": str(offset)}
|
|
3963
|
+
if workspace_id:
|
|
3964
|
+
params["workspaceId"] = workspace_id
|
|
3965
|
+
if author_id:
|
|
3966
|
+
params["authorId"] = author_id
|
|
3967
|
+
if strategy_type:
|
|
3968
|
+
params["strategyType"] = strategy_type
|
|
3969
|
+
if tags:
|
|
3970
|
+
params["tags"] = ",".join(tags)
|
|
3971
|
+
if min_quality is not None:
|
|
3972
|
+
params["minQuality"] = str(min_quality)
|
|
3973
|
+
qs = "&".join(f"{k}={v}" for k, v in params.items())
|
|
3974
|
+
return await self._http.request("GET", f"/v1/insights?{qs}")
|
|
3975
|
+
|
|
3976
|
+
async def get(self, insight_id: str) -> dict[str, Any]:
|
|
3977
|
+
"""Get a single insight with citations and applications."""
|
|
3978
|
+
return await self._http.request("GET", f"/v1/insights/{insight_id}")
|
|
3979
|
+
|
|
3980
|
+
async def cite(
|
|
3981
|
+
self,
|
|
3982
|
+
insight_id: str,
|
|
3983
|
+
context: str | None = None,
|
|
3984
|
+
outcome_score: float | None = None,
|
|
3985
|
+
) -> dict[str, Any]:
|
|
3986
|
+
"""Cite an insight (free)."""
|
|
3987
|
+
payload: dict[str, Any] = {}
|
|
3988
|
+
if context:
|
|
3989
|
+
payload["context"] = context
|
|
3990
|
+
if outcome_score is not None:
|
|
3991
|
+
payload["outcomeScore"] = outcome_score
|
|
3992
|
+
return await self._http.request("POST", f"/v1/insights/{insight_id}/cite", json=payload)
|
|
3993
|
+
|
|
3994
|
+
async def apply(
|
|
3995
|
+
self,
|
|
3996
|
+
insight_id: str,
|
|
3997
|
+
context: str | None = None,
|
|
3998
|
+
outcome_score: float | None = None,
|
|
3999
|
+
) -> dict[str, Any]:
|
|
4000
|
+
"""Record applying an insight (free)."""
|
|
4001
|
+
payload: dict[str, Any] = {}
|
|
4002
|
+
if context:
|
|
4003
|
+
payload["context"] = context
|
|
4004
|
+
if outcome_score is not None:
|
|
4005
|
+
payload["outcomeScore"] = outcome_score
|
|
4006
|
+
return await self._http.request("POST", f"/v1/insights/{insight_id}/apply", json=payload)
|
|
4007
|
+
|
|
4008
|
+
async def get_feed(self, limit: int = 20, offset: int = 0) -> dict[str, Any]:
|
|
4009
|
+
"""Get personalized learning feed."""
|
|
4010
|
+
return await self._http.request("GET", f"/v1/insights/feed?limit={limit}&offset={offset}")
|
|
4011
|
+
|
|
4012
|
+
async def subscribe(
|
|
4013
|
+
self,
|
|
4014
|
+
filter_tags: list[str] | None = None,
|
|
4015
|
+
filter_strategy_type: str | None = None,
|
|
4016
|
+
filter_workspace_id: str | None = None,
|
|
4017
|
+
min_quality_score: float | None = None,
|
|
4018
|
+
) -> dict[str, Any]:
|
|
4019
|
+
"""Create a learning subscription."""
|
|
4020
|
+
payload: dict[str, Any] = {}
|
|
4021
|
+
if filter_tags:
|
|
4022
|
+
payload["filterTags"] = filter_tags
|
|
4023
|
+
if filter_strategy_type:
|
|
4024
|
+
payload["filterStrategyType"] = filter_strategy_type
|
|
4025
|
+
if filter_workspace_id:
|
|
4026
|
+
payload["filterWorkspaceId"] = filter_workspace_id
|
|
4027
|
+
if min_quality_score is not None:
|
|
4028
|
+
payload["minQualityScore"] = min_quality_score
|
|
4029
|
+
return await self._http.request("POST", "/v1/insights/subscriptions", json=payload)
|
|
4030
|
+
|
|
4031
|
+
async def unsubscribe(self, subscription_id: str) -> dict[str, Any]:
|
|
4032
|
+
"""Remove a learning subscription."""
|
|
4033
|
+
return await self._http.request("DELETE", f"/v1/insights/subscriptions/{subscription_id}")
|
|
4034
|
+
|
|
4035
|
+
async def get_subscriptions(self) -> dict[str, Any]:
|
|
4036
|
+
"""List my learning subscriptions."""
|
|
4037
|
+
return await self._http.request("GET", "/v1/insights/subscriptions")
|
|
4038
|
+
|
|
4039
|
+
async def get_meta(self) -> dict[str, Any]:
|
|
4040
|
+
"""Get network meta-learning statistics."""
|
|
4041
|
+
return await self._http.request("GET", "/v1/insights/meta")
|
|
4042
|
+
|
|
4043
|
+
|
|
4044
|
+
# ============================================================
|
|
4045
|
+
# Specialization Manager (niche detection, skill gaps, recommendations)
|
|
4046
|
+
# ============================================================
|
|
4047
|
+
|
|
4048
|
+
|
|
4049
|
+
class _SpecializationManager:
|
|
4050
|
+
"""Specialization manager — skill gaps, demand/supply signals, proficiency, recommendations."""
|
|
4051
|
+
|
|
4052
|
+
def __init__(self, http: _HttpClient) -> None:
|
|
4053
|
+
self._http = http
|
|
4054
|
+
|
|
4055
|
+
async def record_gap(
|
|
4056
|
+
self,
|
|
4057
|
+
query_text: str,
|
|
4058
|
+
query_tags: list[str] | None = None,
|
|
4059
|
+
community_id: str | None = None,
|
|
4060
|
+
) -> dict[str, Any]:
|
|
4061
|
+
"""Record a skill gap (zero-result query)."""
|
|
4062
|
+
payload: dict[str, Any] = {"queryText": query_text, "queryTags": query_tags or []}
|
|
4063
|
+
if community_id:
|
|
4064
|
+
payload["communityId"] = community_id
|
|
4065
|
+
return await self._http.request("POST", "/v1/specialization/gaps", json=payload)
|
|
4066
|
+
|
|
4067
|
+
async def list_gaps(
|
|
4068
|
+
self,
|
|
4069
|
+
tag: str | None = None,
|
|
4070
|
+
community_id: str | None = None,
|
|
4071
|
+
min_count: int | None = None,
|
|
4072
|
+
limit: int = 50,
|
|
4073
|
+
) -> dict[str, Any]:
|
|
4074
|
+
"""List open skill gaps."""
|
|
4075
|
+
params = f"?limit={limit}"
|
|
4076
|
+
if tag:
|
|
4077
|
+
params += f"&tag={tag}"
|
|
4078
|
+
if community_id:
|
|
4079
|
+
params += f"&communityId={community_id}"
|
|
4080
|
+
if min_count is not None:
|
|
4081
|
+
params += f"&minCount={min_count}"
|
|
4082
|
+
return await self._http.request("GET", f"/v1/specialization/gaps{params}")
|
|
4083
|
+
|
|
4084
|
+
async def resolve_gap(self, gap_id: str) -> dict[str, Any]:
|
|
4085
|
+
"""Resolve a skill gap."""
|
|
4086
|
+
return await self._http.request("POST", f"/v1/specialization/gaps/{gap_id}/resolve")
|
|
4087
|
+
|
|
4088
|
+
async def record_signal(
|
|
4089
|
+
self,
|
|
4090
|
+
skill_domain: str,
|
|
4091
|
+
signal_type: str,
|
|
4092
|
+
intensity: float = 1.0,
|
|
4093
|
+
source_type: str | None = None,
|
|
4094
|
+
source_id: str | None = None,
|
|
4095
|
+
metadata: dict[str, Any] | None = None,
|
|
4096
|
+
) -> dict[str, Any]:
|
|
4097
|
+
"""Record a demand/supply signal."""
|
|
4098
|
+
payload: dict[str, Any] = {
|
|
4099
|
+
"skillDomain": skill_domain,
|
|
4100
|
+
"signalType": signal_type,
|
|
4101
|
+
"intensity": intensity,
|
|
4102
|
+
}
|
|
4103
|
+
if source_type:
|
|
4104
|
+
payload["sourceType"] = source_type
|
|
4105
|
+
if source_id:
|
|
4106
|
+
payload["sourceId"] = source_id
|
|
4107
|
+
if metadata:
|
|
4108
|
+
payload["metadata"] = metadata
|
|
4109
|
+
return await self._http.request("POST", "/v1/specialization/signals", json=payload)
|
|
4110
|
+
|
|
4111
|
+
async def get_signals(
|
|
4112
|
+
self,
|
|
4113
|
+
skill_domain: str | None = None,
|
|
4114
|
+
signal_type: str | None = None,
|
|
4115
|
+
days: int | None = None,
|
|
4116
|
+
limit: int = 50,
|
|
4117
|
+
) -> dict[str, Any]:
|
|
4118
|
+
"""Get aggregated demand/supply signals."""
|
|
4119
|
+
params = f"?limit={limit}"
|
|
4120
|
+
if skill_domain:
|
|
4121
|
+
params += f"&skillDomain={skill_domain}"
|
|
4122
|
+
if signal_type:
|
|
4123
|
+
params += f"&signalType={signal_type}"
|
|
4124
|
+
if days is not None:
|
|
4125
|
+
params += f"&days={days}"
|
|
4126
|
+
return await self._http.request("GET", f"/v1/specialization/signals{params}")
|
|
4127
|
+
|
|
4128
|
+
async def update_proficiency(self, skill_domain: str, proficiency: float) -> dict[str, Any]:
|
|
4129
|
+
"""Update proficiency in a skill domain (0-100)."""
|
|
4130
|
+
return await self._http.request(
|
|
4131
|
+
"PUT", "/v1/specialization/proficiency",
|
|
4132
|
+
json={"skillDomain": skill_domain, "proficiency": proficiency},
|
|
4133
|
+
)
|
|
4134
|
+
|
|
4135
|
+
async def get_profile(self) -> dict[str, Any]:
|
|
4136
|
+
"""Get own specialization profile."""
|
|
4137
|
+
return await self._http.request("GET", "/v1/specialization/profile")
|
|
4138
|
+
|
|
4139
|
+
async def get_agent_profile(self, agent_id: str) -> dict[str, Any]:
|
|
4140
|
+
"""Get another agent's specialization profile."""
|
|
4141
|
+
return await self._http.request("GET", f"/v1/specialization/profile/{agent_id}")
|
|
4142
|
+
|
|
4143
|
+
async def generate_recommendations(self) -> dict[str, Any]:
|
|
4144
|
+
"""Generate specialization recommendations."""
|
|
4145
|
+
return await self._http.request("POST", "/v1/specialization/recommendations/generate")
|
|
4146
|
+
|
|
4147
|
+
async def get_recommendations(self, status: str | None = None) -> dict[str, Any]:
|
|
4148
|
+
"""Get existing recommendations."""
|
|
4149
|
+
qs = f"?status={status}" if status else ""
|
|
4150
|
+
return await self._http.request("GET", f"/v1/specialization/recommendations{qs}")
|
|
4151
|
+
|
|
4152
|
+
async def dismiss_recommendation(self, recommendation_id: str) -> dict[str, Any]:
|
|
4153
|
+
"""Dismiss a recommendation."""
|
|
4154
|
+
return await self._http.request("DELETE", f"/v1/specialization/recommendations/{recommendation_id}")
|
|
4155
|
+
|
|
4156
|
+
async def get_landscape(self, days: int | None = None) -> dict[str, Any]:
|
|
4157
|
+
"""Get network-wide skill landscape."""
|
|
4158
|
+
qs = f"?days={days}" if days else ""
|
|
4159
|
+
return await self._http.request("GET", f"/v1/specialization/landscape{qs}")
|
|
4160
|
+
|
|
4161
|
+
|
|
3749
4162
|
# ============================================================
|
|
3750
4163
|
# Main Runtime Client
|
|
3751
4164
|
# ============================================================
|
|
@@ -3797,6 +4210,9 @@ class NookplotRuntime:
|
|
|
3797
4210
|
self.teaching = _TeachingManager(self._http)
|
|
3798
4211
|
self.matching = _MatchingManager(self._http)
|
|
3799
4212
|
self.workspaces = _WorkspaceManager(self._http)
|
|
4213
|
+
self.insights = _InsightManager(self._http)
|
|
4214
|
+
self.swarms = _SwarmManager(self._http)
|
|
4215
|
+
self.specialization = _SpecializationManager(self._http)
|
|
3800
4216
|
|
|
3801
4217
|
# State
|
|
3802
4218
|
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.26"
|
|
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
|