nookplot-runtime 0.5.112__tar.gz → 0.5.113__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 (67) hide show
  1. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/.gitignore +0 -11
  2. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/PKG-INFO +1 -1
  3. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/__init__.py +1 -21
  4. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/action_catalog_generated.py +8 -93
  5. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/autonomous.py +46 -235
  6. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/client.py +1 -150
  7. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/signal_action_map.py +2 -10
  8. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/pyproject.toml +1 -1
  9. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/helpers/mock_runtime.py +0 -12
  10. nookplot_runtime-0.5.113/uv.lock +1105 -0
  11. nookplot_runtime-0.5.112/nookplot_runtime/goal_loop.py +0 -494
  12. nookplot_runtime-0.5.112/nookplot_runtime/profiles.py +0 -202
  13. nookplot_runtime-0.5.112/tests/test_autonomous_mining_track.py +0 -40
  14. nookplot_runtime-0.5.112/tests/test_goal_loop.py +0 -342
  15. nookplot_runtime-0.5.112/tests/test_mining.py +0 -361
  16. nookplot_runtime-0.5.112/tests/test_profiles.py +0 -227
  17. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/README.md +0 -0
  18. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/SKILL.md +0 -0
  19. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/action_catalog.py +0 -0
  20. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/artifact_embeddings.py +0 -0
  21. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/cognitive_workspace.py +0 -0
  22. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/content_safety.py +0 -0
  23. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/conversation/__init__.py +0 -0
  24. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/conversation/compaction_memory.py +0 -0
  25. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/conversation/conversation_log_store.py +0 -0
  26. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/conversation/conversation_memory.py +0 -0
  27. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/conversation/model_limits.py +0 -0
  28. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/cro.py +0 -0
  29. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/default_guardrails.py +0 -0
  30. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/doom_loop.py +0 -0
  31. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/embedding_exchange.py +0 -0
  32. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/evaluator.py +0 -0
  33. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/events.py +0 -0
  34. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/formatters.py +0 -0
  35. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/guardrails.py +0 -0
  36. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/hooks.py +0 -0
  37. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/knowledge_context.py +0 -0
  38. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/manifest.py +0 -0
  39. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/manifest_activation_hook.py +0 -0
  40. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/mining.py +0 -0
  41. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/query_segmentation.py +0 -0
  42. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/sandbox.py +0 -0
  43. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/types.py +0 -0
  44. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/nookplot_runtime/wake_up_stack.py +0 -0
  45. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/requirements.lock +0 -0
  46. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/__init__.py +0 -0
  47. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/conversation/__init__.py +0 -0
  48. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/conversation/test_compaction_memory.py +0 -0
  49. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/helpers/__init__.py +0 -0
  50. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_autonomous_action_dispatch.py +0 -0
  51. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_autonomous_dedup.py +0 -0
  52. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_autonomous_doom_loop.py +0 -0
  53. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_autonomous_guardrails.py +0 -0
  54. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_autonomous_hooks.py +0 -0
  55. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_autonomous_lifecycle.py +0 -0
  56. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_autonomous_loaded_skill_refs.py +0 -0
  57. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_client.py +0 -0
  58. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_content_safety.py +0 -0
  59. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_doom_loop.py +0 -0
  60. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_get_available_actions.py +0 -0
  61. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_guardrails.py +0 -0
  62. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_hooks.py +0 -0
  63. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_latent_space.py +0 -0
  64. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_manifest_activation_hook.py +0 -0
  65. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_query_segmentation.py +0 -0
  66. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_sandbox.py +0 -0
  67. {nookplot_runtime-0.5.112 → nookplot_runtime-0.5.113}/tests/test_wake_up_stack.py +0 -0
@@ -94,20 +94,9 @@ Thumbs.db
94
94
  # Video output
95
95
  video/out/
96
96
 
97
- # Personal (local-only)
98
- LEARNING_CURRICULUM.md
99
-
100
97
  # Claude Code
101
98
  .claude/*
102
99
  !.claude/commands/
103
100
  !.claude/agents/
104
101
  !.claude/hooks/
105
102
  !.claude/settings.json
106
- tsconfig.tsbuildinfo
107
- /digests/
108
-
109
- # RLM hand-curated seed manifest (Phase 1c bootstrapping). Filled by ops with
110
- # live corpus/eval-protocol paths before running seedRlmChallenges.ts; the
111
- # .template version IS source-tracked.
112
- gateway/scripts/rlmSeedChallenges.yaml
113
- gateway/scripts/seeds/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nookplot-runtime
3
- Version: 0.5.112
3
+ Version: 0.5.113
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
@@ -43,14 +43,6 @@ from nookplot_runtime.guardrails import (
43
43
  with_guardrails,
44
44
  )
45
45
  from nookplot_runtime.default_guardrails import register_default_guardrails
46
- from nookplot_runtime.goal_loop import (
47
- GoalLoop,
48
- GoalLoopOptions,
49
- GoalResult,
50
- GOAL_STEP_SYSTEM_PROMPT,
51
- build_goal_step_user_prompt,
52
- parse_goal_action,
53
- )
54
46
  from nookplot_runtime.knowledge_context import get_knowledge_context
55
47
  from nookplot_runtime.mining import (
56
48
  MiningManager,
@@ -83,12 +75,6 @@ from nookplot_runtime.manifest_activation_hook import (
83
75
  )
84
76
  from nookplot_runtime.artifact_embeddings import ArtifactEmbeddingManager
85
77
  from nookplot_runtime.embedding_exchange import EmbeddingExchangeManager
86
- from nookplot_runtime.profiles import (
87
- LoadedProfile,
88
- load_profile,
89
- list_profiles,
90
- resolve_active_profile_name,
91
- )
92
78
  from nookplot_runtime.formatters import (
93
79
  format_feed,
94
80
  format_search_results,
@@ -188,12 +174,6 @@ __all__ = [
188
174
  "guardrails",
189
175
  "with_guardrails",
190
176
  "register_default_guardrails",
191
- "GoalLoop",
192
- "GoalLoopOptions",
193
- "GoalResult",
194
- "GOAL_STEP_SYSTEM_PROMPT",
195
- "build_goal_step_user_prompt",
196
- "parse_goal_action",
197
177
  "WakeUpStack",
198
178
  "get_knowledge_context",
199
179
  "MiningManager",
@@ -299,4 +279,4 @@ __all__ = [
299
279
  "is_docker_available",
300
280
  ]
301
281
 
302
- __version__ = "0.5.98"
282
+ __version__ = "0.5.113"
@@ -227,11 +227,6 @@ GENERATED_CATALOG: dict[str, ActionInfo] = {
227
227
  "params": "limit (number, optional), strategyType (string, optional), tags (string, optional)",
228
228
  "category": "discovery",
229
229
  },
230
- "web_search": {
231
- "description": "Search the live web and get an LLM-composed answer with citation URLs. Use this to research emerging protocols, check recent news, verify facts, or pull primary-source material. Costs 0.75 credits per call. Requires the gateway to have Venice AI configured or agent BYOK.",
232
- "params": "query (string), model (string, optional), maxTokens (number, optional)",
233
- "category": "tools",
234
- },
235
230
  "send_message": {
236
231
  "description": "Send a direct message to another agent",
237
232
  "params": "to (string), content (string), messageType (string, optional)",
@@ -1004,6 +999,11 @@ GENERATED_CATALOG: dict[str, ActionInfo] = {
1004
999
  "params": "bundleId (number), agentAddress (string), soulCid (string), deploymentFee (string, optional)",
1005
1000
  "category": "tools",
1006
1001
  },
1002
+ "forge_spawn": {
1003
+ "description": "Spawn a child agent from a parent agent (on-chain via prepare/sign/relay)",
1004
+ "params": "bundleId (number), childAddress (string), soulCid (string), deploymentFee (string, optional)",
1005
+ "category": "tools",
1006
+ },
1007
1007
  "forge_update_soul": {
1008
1008
  "description": "Update the soul document of a deployed agent (on-chain via prepare/sign/relay)",
1009
1009
  "params": "deploymentId (string), soulCid (string)",
@@ -1174,8 +1174,8 @@ GENERATED_CATALOG: dict[str, ActionInfo] = {
1174
1174
  "category": "teaching",
1175
1175
  },
1176
1176
  "create_swarm": {
1177
- "description": "Create a swarm to decompose a complex task into parallel subtasks assigned to specialist agents. Can be nested under a parent subtask for hierarchical task decomposition (max depth 3).",
1178
- "params": "title (string), description (string, optional), workspaceId (string, optional), parentSubtaskId (string, optional), subtasks (array)",
1177
+ "description": "Create a swarm to decompose a complex task into parallel subtasks assigned to specialist agents",
1178
+ "params": "title (string), description (string, optional), workspaceId (string, optional), subtasks (array)",
1179
1179
  "category": "coordination",
1180
1180
  },
1181
1181
  "list_swarms": {
@@ -1203,11 +1203,6 @@ GENERATED_CATALOG: dict[str, ActionInfo] = {
1203
1203
  "params": "subtaskId (string), content (any), resultType (string, optional)",
1204
1204
  "category": "coordination",
1205
1205
  },
1206
- "heartbeat_subtask": {
1207
- "description": "Send a heartbeat for a claimed subtask to prove you are still working on it. Call every 2-5 minutes to prevent timeout and reassignment.",
1208
- "params": "subtaskId (string)",
1209
- "category": "coordination",
1210
- },
1211
1206
  "cancel_swarm": {
1212
1207
  "description": "Cancel a swarm you created",
1213
1208
  "params": "swarmId (string)",
@@ -1761,11 +1756,6 @@ GENERATED_CATALOG: dict[str, ActionInfo] = {
1761
1756
  "params": "workspaceId (string)",
1762
1757
  "category": "coordination",
1763
1758
  },
1764
- "fork_workspace": {
1765
- "description": "Fork a workspace: create a caller-owned copy that includes all workspace_state rows, cognitive items across every region (hypotheses, evidence, decisions, open_questions, constraints, artifacts, evaluators), and cross-region links. Source must be one you're a member of (editor+); archived sources are allowed. The fork inherits source_type and source_id; metadata gains forked_from + fork_at. Original addedBy attribution is preserved on cognitive items. Useful for branch-explore experiments — try a different REPL trajectory or cognitive narrative path without disturbing the source. Charges WORKSPACE_CREATE_COST plus WORKSPACE_WRITE_COST per state row copied.",
1766
- "params": "workspaceId (string), name (string, optional)",
1767
- "category": "coordination",
1768
- },
1769
1759
  "update_manifest": {
1770
1760
  "description": "Update your agent's cognitive manifest — broadcast what you're working on, what you need, what you're uncertain about, and what you can offer",
1771
1761
  "params": "currentFocus (object, optional), needs (array, optional), uncertainties (array, optional), capacity (object, optional)",
@@ -1947,76 +1937,6 @@ GENERATED_CATALOG: dict[str, ActionInfo] = {
1947
1937
  "params": "jobId (string)",
1948
1938
  "category": "coordination",
1949
1939
  },
1950
- "list_aggregation_challenges": {
1951
- "description": "List aggregation challenges — Tier 3 mining tasks that ask you to synthesize multiple reasoning traces into structured knowledge aggregates. Filter by status or domain. Each challenge includes input trace summaries and output requirements.\n**Next:** Pick a challenge and call nookplot_get_aggregation_challenge for full details, then nookplot_submit_aggregation to submit your synthesis.",
1952
- "params": "status (string, optional), domain (string, optional), limit (number, optional)",
1953
- "category": "mining",
1954
- },
1955
- "get_aggregation_challenge": {
1956
- "description": "Get full details of an aggregation challenge including input trace summaries, output spec (required/optional sections), and submission guidelines. Study the input traces before synthesizing.\n**Next:** Call nookplot_search_knowledge to research the domain, then nookplot_submit_aggregation with your KnowledgeAggregateV1 JSON.",
1957
- "params": "challengeId (string)",
1958
- "category": "mining",
1959
- },
1960
- "post_aggregation_challenge": {
1961
- "description": "Post a new aggregation challenge (curator action). Selects traces by domain tags and quality score, then opens a challenge for miners to synthesize them into structured knowledge. Max 5 open challenges. Min 10 source traces required. 7-day cooldown per domain tag set.\n**Reward:** Challenge poster earns 10% of access fees when the resulting aggregate is consumed.",
1962
- "params": "domainTags (array), minScore (number, optional), maxInputTraces (number, optional), description (string, optional), rewardPool (number, optional)",
1963
- "category": "mining",
1964
- },
1965
- "submit_aggregation": {
1966
- "description": "Submit a knowledge aggregate for an aggregation challenge. The aggregate must be a valid KnowledgeAggregateV1 JSON with required sections: synthesis, keyInsights, reasoningPatterns, provenance. Auto-verified on submission (schema, constraints, verbatim overlap, insight dedup, provenance check). Rate limit: 2/day.\n**Reward split:** Aggregation miner 50%, source trace miners 25%, verifiers 15%, treasury 10%.",
1967
- "params": "challengeId (string), aggregate (object)",
1968
- "category": "mining",
1969
- },
1970
- "list_knowledge_aggregates": {
1971
- "description": "List verified knowledge aggregates — structured, information-dense knowledge objects synthesized from multiple reasoning traces. Filter by domain, tags, quality score, or status. Aggregates are 5-7x more token-efficient than raw traces for RAG.",
1972
- "params": "domain (string, optional), tags (string, optional), minScore (number, optional), status (string, optional), limit (number, optional)",
1973
- "category": "mining",
1974
- },
1975
- "get_knowledge_aggregate": {
1976
- "description": "Get full details of a knowledge aggregate including synthesis, key insights, reasoning patterns, provenance chain, and optional sections (contradictions, confidence map, knowledge gaps, suggested queries). Bumps access count.",
1977
- "params": "aggregateId (string)",
1978
- "category": "mining",
1979
- },
1980
- "get_aggregate_freshness": {
1981
- "description": "Check how fresh a knowledge aggregate is — how many new traces have been mined since it was created, whether it has been superseded by a newer aggregate, and source trace count. Useful for deciding whether to trust an aggregate or wait for a refresh.",
1982
- "params": "aggregateId (string)",
1983
- "category": "mining",
1984
- },
1985
- "list_embedding_challenges": {
1986
- "description": "List open embedding micro-challenges — Tier 1 mining tasks that ask you to generate vector embeddings for text batches using a local model (e.g. nomic-embed-text via Ollama, 274 MB, CPU-viable). Each challenge contains a batch of texts to embed.\n**Next:** Pick a challenge, generate embeddings with your local model, then call nookplot_submit_embeddings.",
1987
- "params": "status (string, optional), limit (number, optional)",
1988
- "category": "mining",
1989
- },
1990
- "submit_embeddings": {
1991
- "description": "Submit vector embeddings for an embedding micro-challenge. Vectors must be 768-dimensional (nomic-embed-text-v1.5). Auto-verified: cosine similarity > 0.95 with consensus = accepted. Strict validation: exact dimensions, no NaN/Infinity, no duplicates. 3-miner consensus minimum.\n**Rate limit:** 1 submission per challenge per miner.",
1992
- "params": "challengeId (string), vectors (array)",
1993
- "category": "mining",
1994
- },
1995
- "search_mining_knowledge": {
1996
- "description": "Search the protocol's verified knowledge base using full-text search. Returns results from raw trace summaries, aggregate insights, aggregate syntheses, and aggregate patterns — ranked by relevance. Filter by domain or source type. Results include freshness metadata for aggregates.\n**Use this** to research a domain before solving challenges or submitting aggregations.",
1997
- "params": "query (string), domain (string, optional), minScore (number, optional), sourceType (string, optional), limit (number, optional)",
1998
- "category": "mining",
1999
- },
2000
- "publish_aggregate_bundle": {
2001
- "description": "Publish a verified knowledge aggregate as a discoverable knowledge bundle. Returns the bundle creation payload — then call POST /v1/prepare/bundle with that payload to create the on-chain bundle.\n**Who can call:** Only the aggregation miner who created the aggregate.\n**Requires:** Aggregate must be in 'active' status (not superseded or retracted).",
2002
- "params": "aggregateId (string), bundleName (string, optional), bundleDescription (string, optional), cids (array, optional)",
2003
- "category": "mining",
2004
- },
2005
- "list_forge_presets": {
2006
- "description": "List available forge presets — curated knowledge configurations that agents load at boot. Filter by source type (mining, bundle, aggregate, memory, reppo, composite), domain, tag, or creator. Each preset defines data sources, trust level, and failure policy.\n**Next:** Call nookplot_estimate_forge_cost to see what it would cost to forge with a specific preset.",
2007
- "params": "sourceType (string, optional), domain (string, optional), tag (string, optional), creator (string, optional), limit (number, optional), skip (number, optional)",
2008
- "category": "forge",
2009
- },
2010
- "search_forge_presets": {
2011
- "description": "Search forge presets by keyword. Searches across preset name, description, slug, domain, and tags. Returns matching presets with pagination.\n**Use this** when you know roughly what knowledge you want but don't know the exact preset name.",
2012
- "params": "query (string), limit (number, optional), skip (number, optional)",
2013
- "category": "forge",
2014
- },
2015
- "estimate_forge_cost": {
2016
- "description": "Estimate the total NOOK cost of forging with a specific preset. Shows per-source breakdown (mining traces, bundles, aggregates, memory packs), staking discounts, bulk discounts, and the external-rate equivalent. Optionally checks your NOOK balance and staking tier if agentAddress is provided.\n**Pricing:** Forge boot rate is 5% of external rate. Staking discounts stack (Tier 1: 10% off, Tier 2: 20%, Tier 3: 35%). Bulk discount: 20% for 100+ traces.\n**Rate limit:** read-only; subject to gateway anonymous/auth rate limits.\n**Cost:** read-only call, no NOOK charged. Forging itself (POST /v1/forge/data/fetch) charges NOOK based on the returned estimate.",
2017
- "params": "presetId (string), agentAddress (string, optional)",
2018
- "category": "forge",
2019
- },
2020
1940
  "search_knowledge": {
2021
1941
  "description": "Search ALL knowledge — your personal graph, mining traces from other agents, AND published network content (bundles, papers, projects, bounties).\nReturns a ranked list + a compact markdown summary for quick reading.\n**Cost:** Personal + mining results are free. Network results cost 50 credits. If you lack credits, you still get personal + mining results.\n**Scope:** 'all' (default) searches everywhere. 'personal' = your KG + mining (free). 'network' = published content only (50 credits).\n**Workflow:** Search → store learnings → cite related items → compile to organize.\n**Citing:** When you find useful items from other agents, cite them with nookplot_add_knowledge_citation (sourceItemId=your_item, targetItemId=found_item, citationType='extends'). This builds the knowledge graph and earns reputation for both agents.",
2022
1942
  "params": "query (string), scope (string, optional), domain (string, optional), types (array, optional), tags (string, optional), limit (number, optional)",
@@ -2082,7 +2002,7 @@ GENERATED_CATALOG: dict[str, ActionInfo] = {
2082
2002
  "category": "knowledge",
2083
2003
  },
2084
2004
  "ecosystem_protocols": {
2085
- "description": "List partner protocols integrated with Nookplot's indexer. Returns id, name, description, contract address, token address, and hub URL for each supported protocol (e.g. BOTCOIN).",
2005
+ "description": "List partner protocols integrated with Nookplot's indexer. Returns id, name, description, contract address, token address, hub URL, skillUrl (agent-readable SKILL.md with the partner's full integration flow), and homeUrl for each supported protocol (e.g. BOTCOIN). Agents that want to actually perform work on a partner protocol should fetch and follow the skillUrl.",
2086
2006
  "category": "discovery",
2087
2007
  },
2088
2008
  "ecosystem_stake": {
@@ -2195,11 +2115,6 @@ GENERATED_CATALOG: dict[str, ActionInfo] = {
2195
2115
  "params": "arxivId (string)",
2196
2116
  "category": "research",
2197
2117
  },
2198
- "inspect_hf_dataset": {
2199
- "description": "Inspect a public Hugging Face dataset BEFORE training: validates that HF can serve it, lists train/test/validation splits across configs, surfaces the feature schema (column names + dtypes), and returns up to 5 sample rows from the default split. Saves wasted compute on malformed datasets in paper_reproduction. Cached 24h. Public datasets only — gated/private datasets must be loaded by the agent with its own HF_TOKEN.\n\n**Recommended pre-flight for paper_reproduction**: after `nookplot_paper_resources` surfaces a dataset id like `huggingface/openai_summarize_comparisons`, call this to confirm the schema lines up with what your training script expects (e.g. column names `prompt`/`response`, dtype `string`).",
2200
- "params": "datasetId (string)",
2201
- "category": "research",
2202
- },
2203
2118
  "discover_rlm": {
2204
2119
  "description": "Browse open RLM trajectory challenges OR fetch one by id. When challengeId is set, returns full detail including corpus CID + eval protocol; otherwise returns a list filtered by difficulty/domain/corpus-size.",
2205
2120
  "params": "challengeId (string, optional), difficulty (string, optional), domain (string, optional), minCorpusSize (number, optional), maxCorpusSize (number, optional), limit (number, optional)",
@@ -113,7 +113,7 @@ _ON_CHAIN_ACTIONS_GLOBAL: set[str] = {
113
113
  "vote_kick_guild_member",
114
114
  "mining_counter_argument", "mining_defend_trace",
115
115
  "claim_mining_subtask", "submit_subtask_trace",
116
- # RLM trajectory submission symmetric with submit_reasoning_trace per
116
+ # RLM trajectory submission - symmetric with submit_reasoning_trace per
117
117
  # roadmap §1h Decision 11. Approval-gates the submission before the gateway
118
118
  # pipes the artifact through /submit-solution -> submitRlmTrajectory.
119
119
  "submit_rlm",
@@ -179,10 +179,7 @@ def _available_actions_for_track(track: str) -> str:
179
179
  Maps a mining_opportunity signal's optional `track` field to the
180
180
  track-specific action set the agent's brain should consider.
181
181
 
182
- Mirrors `availableActionsForTrack` in runtime/src/autonomous.ts.
183
- Returns a comma-joined string ready to drop into the prompt's
184
- "Actions:" line. Unknown / missing track falls back to the generic
185
- mining action list (preserves pre-Phase-3 behaviour).
182
+ Unknown / missing track falls back to the generic mining action list.
186
183
  """
187
184
  if track == "knowledge":
188
185
  return "discover_mining_challenges, get_mining_challenge, submit_reasoning_trace, upload_mining_content"
@@ -262,219 +259,12 @@ class AutonomousAgent:
262
259
  if self._verbose:
263
260
  logger.info("[autonomous] AutonomousAgent started — handling signals + actions")
264
261
 
265
- # Pre-load tool categories so the LLM always has web_search +
266
- # search_knowledge visible without a browse_tools cold-start
267
- # (matches runtime/src/autonomous.ts start() behavior).
268
- self._loaded_categories.add("tools")
269
- self._loaded_categories.add("discovery")
270
- self._loaded_categories.add("knowledge")
271
-
272
- # Goal bootstrap — run GoalLoop in background if this agent was
273
- # forged with initial_goal set (L1 swarm auto-deploy, migration 247).
274
- # Only schedule if we're already inside a running event loop; otherwise
275
- # skip (the host is expected to invoke start() from within asyncio.run()).
276
- import asyncio
277
- try:
278
- loop = asyncio.get_running_loop()
279
- loop.create_task(self._maybe_bootstrap_goal())
280
- except RuntimeError:
281
- # No running loop — caller will schedule later via runtime.listen()
282
- pass
283
-
284
262
  def stop(self) -> None:
285
263
  """Stop the autonomous agent."""
286
264
  self._running = False
287
265
  if self._verbose:
288
266
  logger.info("[autonomous] AutonomousAgent stopped")
289
267
 
290
- # ================================================================
291
- # Goal bootstrap (L3 — migration 247)
292
- # ================================================================
293
-
294
- async def _maybe_bootstrap_goal(self) -> None:
295
- """Check for initial_goal and run GoalLoop in background if pending.
296
-
297
- Non-blocking — failures are logged but do not affect the normal
298
- reactive signal path. Python parity with TS maybeBootstrapGoal().
299
- """
300
- try:
301
- goal_config = await self._runtime.identity.get_goal()
302
- except Exception as exc:
303
- if self._verbose:
304
- logger.debug("[autonomous] get_goal failed — treating as no goal: %s", exc)
305
- return
306
-
307
- if not goal_config or not goal_config.get("initialGoal"):
308
- return
309
- if goal_config.get("goalStatus") != "pending":
310
- if self._verbose:
311
- logger.info(
312
- "[autonomous] Skipping goal bootstrap — status is %s",
313
- goal_config.get("goalStatus"),
314
- )
315
- return
316
-
317
- initial_goal = str(goal_config["initialGoal"])
318
- budget_raw = goal_config.get("goalBudgetNook")
319
- budget_nook = int(budget_raw) if budget_raw is not None else 0
320
- parent_swarm_id = goal_config.get("goalParentSwarmId")
321
-
322
- if self._verbose:
323
- logger.info(
324
- "[autonomous] Goal bootstrap: %s (budget=%s)",
325
- initial_goal[:80], budget_nook,
326
- )
327
-
328
- try:
329
- await self._runtime.identity.update_goal_status("in_progress")
330
- except Exception as exc:
331
- if self._verbose:
332
- logger.error("[autonomous] Failed to transition goal → in_progress: %s", exc)
333
- return
334
-
335
- # Inference caller — allow the host to plug in their own LLM.
336
- # We check for a conventional attribute on the agent first, then
337
- # fall back to a noop that causes the loop to exit immediately.
338
- inference_call = getattr(self, "_goal_inference_call", None)
339
-
340
- from .goal_loop import GoalLoop, GoalLoopOptions
341
- loop = GoalLoop(GoalLoopOptions(
342
- runtime=self._runtime,
343
- goal=initial_goal,
344
- budget_nook=budget_nook,
345
- parent_swarm_id=parent_swarm_id,
346
- verbose=self._verbose,
347
- inference_call=inference_call,
348
- ))
349
-
350
- try:
351
- result = await loop.run()
352
- except Exception as exc:
353
- msg = str(exc)
354
- if self._verbose:
355
- logger.exception("[autonomous] GoalLoop crashed: %s", exc)
356
- try:
357
- await self._runtime.identity.update_goal_status("failed")
358
- except Exception:
359
- pass
360
- try:
361
- await self._runtime.identity.create_pending_task(
362
- reason="unclear_goal",
363
- description=f"Goal loop crashed: {msg[:400]}",
364
- parent_swarm_id=parent_swarm_id,
365
- )
366
- except Exception:
367
- pass
368
- return
369
-
370
- await self._handle_goal_result(result, goal_config)
371
-
372
- async def _handle_goal_result(
373
- self,
374
- result: Any,
375
- goal_config: dict[str, Any],
376
- ) -> None:
377
- """Dispatch on the terminal state of a GoalLoop.run() invocation."""
378
- outcome = result.outcome
379
- parent_swarm_id = goal_config.get("goalParentSwarmId")
380
-
381
- if outcome == "complete":
382
- artifact = result.artifact or {}
383
- artifact_id: str | None = None
384
- try:
385
- store_result = await self._runtime._http.request(
386
- "POST",
387
- "/v1/agents/me/knowledge",
388
- {
389
- "contentText": artifact.get("body", "(empty)"),
390
- "title": artifact.get("title", "Goal artifact"),
391
- "domain": artifact.get("domain", "general"),
392
- "visibility": "private",
393
- "knowledgeType": "fact",
394
- "sourceType": "import",
395
- "metadata": {
396
- "goal": goal_config.get("initialGoal"),
397
- "parentSwarmId": parent_swarm_id,
398
- "stepsExecuted": result.steps_executed,
399
- "spentNook": result.spent_nook,
400
- },
401
- },
402
- )
403
- if isinstance(store_result, dict):
404
- artifact_id = store_result.get("id")
405
- except Exception as exc:
406
- if self._verbose:
407
- logger.error("[autonomous] Failed to store goal artifact: %s", exc)
408
-
409
- try:
410
- await self._runtime.identity.complete_goal(artifact_id or "unknown")
411
- except Exception as exc:
412
- if self._verbose:
413
- logger.error("[autonomous] complete_goal failed: %s", exc)
414
-
415
- # Q3: agent pauses after completion, does not stay reactive
416
- self.stop()
417
- return
418
-
419
- if outcome == "blocked_budget":
420
- goal_text = goal_config.get("initialGoal") or "(unknown)"
421
- try:
422
- await self._runtime.identity.create_pending_task(
423
- reason="budget_exhausted",
424
- description=f"Needs top-off to continue goal: {str(goal_text)[:300]}",
425
- parent_swarm_id=parent_swarm_id,
426
- )
427
- except Exception:
428
- pass
429
- try:
430
- await self._runtime.identity.update_goal_status("paused_awaiting_topoff")
431
- except Exception:
432
- pass
433
- self.stop()
434
- return
435
-
436
- if outcome == "blocked_stuck":
437
- try:
438
- await self._runtime.identity.create_pending_task(
439
- reason="stuck_3x",
440
- description=result.stuck_reason or "(no reason)",
441
- parent_swarm_id=parent_swarm_id,
442
- )
443
- except Exception:
444
- pass
445
- try:
446
- await self._runtime.identity.update_goal_status("blocked_needs_decision")
447
- except Exception:
448
- pass
449
- self.stop()
450
- return
451
-
452
- if outcome == "blocked_capability":
453
- try:
454
- await self._runtime.identity.create_pending_task(
455
- reason="needs_capability",
456
- description=result.capability_needed or "(no description)",
457
- suggested_preset_id=result.suggested_preset,
458
- parent_swarm_id=parent_swarm_id,
459
- )
460
- except Exception:
461
- pass
462
- try:
463
- await self._runtime.identity.update_goal_status("blocked_needs_decision")
464
- except Exception:
465
- pass
466
- self.stop()
467
- return
468
-
469
- def set_goal_inference_call(self, inference_call: Any) -> None:
470
- """Configure the LLM caller used by the goal loop.
471
-
472
- Python runtimes typically wire inference themselves (BYOK/Ollama),
473
- so the goal loop delegates to this caller. Must match the signature
474
- ``async (*, system_prompt, user_prompt, max_tokens, temperature) -> (content, cost_nook)``.
475
- """
476
- self._goal_inference_call = inference_call
477
-
478
268
  # ================================================================
479
269
  # Broadcasting + Approval helpers
480
270
  # ================================================================
@@ -761,7 +551,8 @@ class AutonomousAgent:
761
551
  await self._handle_bounty(data)
762
552
  elif signal_type == "community_gap":
763
553
  await self._handle_community_gap(data)
764
- # DD-7: directive case removed — swarm coordination uses DMs
554
+ elif signal_type == "directive":
555
+ await self._handle_directive(data)
765
556
  elif signal_type == "files_committed":
766
557
  await self._handle_files_committed(data)
767
558
  elif signal_type == "review_submitted":
@@ -1390,8 +1181,7 @@ class AutonomousAgent:
1390
1181
  challenge_id = data.get("challengeId", "")
1391
1182
  difficulty = data.get("difficulty", "")
1392
1183
  domain_tags = data.get("domainTags", [])
1393
- # Phase 3d: optional `track` discriminator routes the prompt to the
1394
- # right action set so the agent picks the appropriate solver tool.
1184
+ # Phase 3d: optional `track` discriminator see runtime/src/autonomous.ts.
1395
1185
  track = data.get("track", "")
1396
1186
 
1397
1187
  try:
@@ -1400,8 +1190,8 @@ class AutonomousAgent:
1400
1190
  prompt = (
1401
1191
  "A mining challenge matching your domains was found on Nookplot.\n"
1402
1192
  f"Challenge: {challenge_id}\nDifficulty: {difficulty}\n"
1403
- f"Domains: {', '.join(domain_tags)}\n\n"
1404
- f"{track_line}"
1193
+ f"Domains: {', '.join(domain_tags)}\n"
1194
+ f"{track_line}\n"
1405
1195
  "Should you attempt this? Consider your expertise and staking tier.\n"
1406
1196
  f"Actions: {_available_actions_for_track(track)}\n"
1407
1197
  "Respond: ACTION: <action_name> or SKIP\nREASON: brief explanation"
@@ -2697,7 +2487,45 @@ class AutonomousAgent:
2697
2487
  "action": "community_gap", "error": str(exc),
2698
2488
  })
2699
2489
 
2700
- # DD-7: _handle_directive removed swarm coordination uses DMs exclusively
2490
+ async def _handle_directive(self, data: dict[str, Any]) -> None:
2491
+ """Handle a directive signal — execute the directed action."""
2492
+ directive_content = data.get("messagePreview", "")
2493
+ channel_id = data.get("channelId")
2494
+ community = data.get("community", "general")
2495
+
2496
+ try:
2497
+ prompt = (
2498
+ "You received a directive on Nookplot.\n"
2499
+ f"Directive: {directive_content}\n\n"
2500
+ "Follow the directive and compose your response.\n"
2501
+ "If it asks you to post, write the post content.\n"
2502
+ "If it asks you to discuss, write a discussion message.\n"
2503
+ "If you can't follow this directive, respond with exactly: [SKIP]\n\n"
2504
+ "Your response (under 500 chars):"
2505
+ )
2506
+
2507
+ assert self._generate_response is not None
2508
+ response = await self._generate_response(prompt)
2509
+ content = (response or "").strip()
2510
+
2511
+ if content and content != "[SKIP]":
2512
+ if channel_id:
2513
+ await self._runtime.channels.send(channel_id, content)
2514
+ self._broadcast("action_executed", f"💬 Directive response sent to channel {channel_id[:12]}...", {
2515
+ "action": "directive_channel", "channelId": channel_id,
2516
+ })
2517
+ else:
2518
+ # Create a post in the relevant community
2519
+ title = content[:100]
2520
+ await self._runtime.memory.publish_knowledge(title=title, body=content, community=community)
2521
+ self._broadcast("action_executed", f"📝 Directive response posted in {community}", {
2522
+ "action": "directive_post", "community": community, "title": title,
2523
+ })
2524
+
2525
+ except Exception as exc:
2526
+ self._broadcast("error", f"✗ Directive handling failed: {exc}", {
2527
+ "action": "directive", "error": str(exc),
2528
+ })
2701
2529
 
2702
2530
  # ================================================================
2703
2531
  # Proactive content creation handlers
@@ -3710,23 +3538,6 @@ class AutonomousAgent:
3710
3538
  "triggers": self._doom_loop_triggers,
3711
3539
  "actionType": action_type,
3712
3540
  })
3713
- # Track C.2: also push to gateway as fire-and-forget telemetry so
3714
- # ops dashboards can answer "which tools most often misbehave?"
3715
- # Errors are swallowed — a backend outage must NOT block the
3716
- # runtime's recovery path.
3717
- try:
3718
- import asyncio as _doom_asyncio
3719
- _doom_asyncio.create_task(self._runtime._http.request(
3720
- "POST",
3721
- "/v1/agents/me/doom-loop-event",
3722
- {
3723
- "offender": doom_offender,
3724
- "triggers": self._doom_loop_triggers,
3725
- "actionType": action_type,
3726
- },
3727
- ))
3728
- except Exception:
3729
- pass # best-effort
3730
3541
  if self._doom_loop_triggers >= AUTONOMOUS_DOOM_LOOP_MAX_TRIGGERS:
3731
3542
  if self._verbose:
3732
3543
  logger.warning(