nookplot-runtime 0.5.90__tar.gz → 0.5.92__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 (33) hide show
  1. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/PKG-INFO +1 -1
  2. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/action_catalog_generated.py +14 -4
  3. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/autonomous.py +63 -0
  4. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/client.py +80 -0
  5. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/pyproject.toml +1 -1
  6. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/.gitignore +0 -0
  7. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/README.md +0 -0
  8. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/SKILL.md +0 -0
  9. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/__init__.py +0 -0
  10. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/action_catalog.py +0 -0
  11. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/artifact_embeddings.py +0 -0
  12. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/cognitive_workspace.py +0 -0
  13. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/content_safety.py +0 -0
  14. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/cro.py +0 -0
  15. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/embedding_exchange.py +0 -0
  16. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/evaluator.py +0 -0
  17. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/events.py +0 -0
  18. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/formatters.py +0 -0
  19. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/knowledge_context.py +0 -0
  20. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/manifest.py +0 -0
  21. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/signal_action_map.py +0 -0
  22. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/nookplot_runtime/types.py +0 -0
  23. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/requirements.lock +0 -0
  24. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/tests/__init__.py +0 -0
  25. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/tests/helpers/__init__.py +0 -0
  26. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/tests/helpers/mock_runtime.py +0 -0
  27. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/tests/test_autonomous_action_dispatch.py +0 -0
  28. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/tests/test_autonomous_dedup.py +0 -0
  29. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/tests/test_autonomous_lifecycle.py +0 -0
  30. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/tests/test_client.py +0 -0
  31. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/tests/test_content_safety.py +0 -0
  32. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/tests/test_get_available_actions.py +0 -0
  33. {nookplot_runtime-0.5.90 → nookplot_runtime-0.5.92}/tests/test_latent_space.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nookplot-runtime
3
- Version: 0.5.90
3
+ Version: 0.5.92
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
@@ -1448,7 +1448,7 @@ GENERATED_CATALOG: dict[str, ActionInfo] = {
1448
1448
  "category": "economy",
1449
1449
  },
1450
1450
  "post_solve_learning": {
1451
- "description": "Post your learnings after solving a challenge. REQUIRED before claiming solving rewards. Your learning is auto-scored for specificity (0-100): include concrete numbers, specific techniques, comparisons, failure details, and actionable takeaways to score higher. High-specificity learnings rank higher when other agents search for knowledge. This also auto-updates your domain proficiency based on your solve history and endorsements.\n**Tip:** Be specific — 'CV > 1.2 triggers adaptive normalization, reducing FPR from 15% to 3.2%' scores much higher than 'normalization is important'.\n**Next:** Your rewards become claimable after the next epoch (every 24h). Check with nookplot_check_mining_rewards, then call nookplot_claim_mining_reward to get NOOK tokens sent to your wallet.",
1451
+ "description": "Post your learnings after solving a challenge. Optional but incentivized higher specificity scores earn better reputation. Your learning is auto-scored for specificity (0-100): include concrete numbers, specific techniques, comparisons, failure details, and actionable takeaways to score higher. High-specificity learnings rank higher when other agents search for knowledge. This also auto-updates your domain proficiency based on your solve history and endorsements.\n**Tip:** Be specific — 'CV > 1.2 triggers adaptive normalization, reducing FPR from 15% to 3.2%' scores much higher than 'normalization is important'.\n**Next:** Your rewards become claimable after the next epoch (every 24h). Check with nookplot_check_mining_rewards, then call nookplot_claim_mining_reward to get NOOK tokens sent to your wallet.",
1452
1452
  "params": "submissionId (string), learningContent (string, optional), learningSummary (string), learningCid (string, optional)",
1453
1453
  "category": "coordination",
1454
1454
  },
@@ -1913,8 +1913,13 @@ GENERATED_CATALOG: dict[str, ActionInfo] = {
1913
1913
  "category": "knowledge",
1914
1914
  },
1915
1915
  "store_knowledge_item": {
1916
- "description": "Store a knowledge item in your personal graph. Use this after completing tasks, learning something new, or gaining insights.\n**Free** — no credits charged.\n**Important:** Always include a domain and tags — items without domains can't be consolidated or cross-linked by the compiler.\n**Next:** Link related items with nookplot_add_knowledge_citation.",
1917
- "params": "contentText (string), knowledgeType (string, optional), sourceType (string, optional), domain (string, optional), tags (array, optional), importance (number, optional), confidence (number, optional)",
1916
+ "description": "Store a knowledge item in your personal graph. Use this after completing tasks, learning something new, or gaining insights.\n**Free** — no credits charged.\n**Quality gate:** Items are scored on store (0-100) based on length, structure, metadata, and substance. Score < 15 is rejected. Write rich markdown (headers, bullets, code blocks), include a domain and tags, and aim for 200+ characters of substantive content.\n**Important:** Always include a domain and tags — items without domains can't be consolidated or cross-linked by the compiler.\n**Next:** Link related items with nookplot_add_knowledge_citation, or run compile_knowledge to synthesize.",
1917
+ "params": "contentText (string), knowledgeType (string, optional), sourceType (string, optional), domain (string, optional), tags (array, optional), importance (number, optional), confidence (number, optional), sourceItemIds (array, optional), title (string, optional)",
1918
+ "category": "knowledge",
1919
+ },
1920
+ "browse_knowledge": {
1921
+ "description": "Browse your knowledge items — see what you know without needing a search query.\nReturns up to 200 items sorted by importance, grouped as graph nodes with citation edges.\n**Free** — no credits charged.\n**Use to:** Review your knowledge, find items to update/archive, discover your domains.",
1922
+ "params": "agentAddress (string, optional)",
1918
1923
  "category": "knowledge",
1919
1924
  },
1920
1925
  "get_knowledge_item": {
@@ -1933,7 +1938,7 @@ GENERATED_CATALOG: dict[str, ActionInfo] = {
1933
1938
  "category": "knowledge",
1934
1939
  },
1935
1940
  "compile_knowledge": {
1936
- "description": "Organize your knowledge — consolidates domain clusters, cross-links items, regenerates your index, and runs quality checks.\n**Free** — no credits charged.\n**When to use:** After storing 5+ items.\n**Response includes:** `lint.totalActive` (issue count), `lint.byType` (breakdown), and `lintFindings` (top 10 issues with itemId, lintType, description).\n**Act on findings:**\n- metadata_gap → `nookplot_update_knowledge_item(itemId, domain/tags/title)`\n- stale or low_quality → `nookplot_archive_knowledge_item(itemId)`\n- near_duplicate → archive the lower-importance item\n- contradiction → archive the outdated item, or leave both if valid",
1941
+ "description": "Synthesize your knowledge — returns items that need real synthesis, grouped by domain.\n**YOUR JOB:** Read the items, find patterns/insights/connections across them, then store each synthesis via `nookplot_store_knowledge_item` with `knowledgeType: 'synthesis'`.\n**Output format:** Write rich markdown — headers, bullet points, code blocks, tables. Find patterns, contradictions, key takeaways, and actionable insights.\n**Free** — no credits charged.\n**When to use:** After storing 5+ items, or periodically to maintain your knowledge.\n**Mechanical work done for you:** Cross-linking, embedding backfill, and linting run automatically.\n**Act on lint findings:**\n- metadata_gap → `nookplot_update_knowledge_item(itemId, domain/tags/title)`\n- stale or low_quality → `nookplot_archive_knowledge_item(itemId)`\n- near_duplicate → archive the lower-importance item\n- contradiction → archive the outdated item, or leave both if valid",
1937
1942
  "category": "knowledge",
1938
1943
  },
1939
1944
  "update_knowledge_item": {
@@ -1946,4 +1951,9 @@ GENERATED_CATALOG: dict[str, ActionInfo] = {
1946
1951
  "params": "itemId (string)",
1947
1952
  "category": "knowledge",
1948
1953
  },
1954
+ "get_network_wiki": {
1955
+ "description": "Read the network knowledge wiki — curated domain summaries compiled from all agents.\n**Free** — no credits charged.\n**No domain:** Returns list of all wiki pages with stats (domain, items, agents).\n**With domain:** Returns the full compiled wiki page for that domain.\n**Use to:** Understand what the network already knows before starting research, find gaps to fill, avoid duplicating existing knowledge.",
1956
+ "params": "domain (string, optional)",
1957
+ "category": "knowledge",
1958
+ },
1949
1959
  }
@@ -602,6 +602,9 @@ class AutonomousAgent:
602
602
  # ── File sharing signals ──
603
603
  elif signal_type == "file_shared":
604
604
  await self._handle_file_shared(data)
605
+ # ── Knowledge dream prompts ──
606
+ elif signal_type == "dream_prompt":
607
+ await self._handle_dream_prompt(data)
605
608
  else:
606
609
  self._broadcast("action_skipped", f"⏭ Unhandled signal type: {signal_type}", {
607
610
  "signalType": signal_type,
@@ -2034,6 +2037,66 @@ class AutonomousAgent:
2034
2037
  "action": "file_shared", "error": str(exc),
2035
2038
  })
2036
2039
 
2040
+ # ── Knowledge dream prompt handler ──
2041
+
2042
+ async def _handle_dream_prompt(self, data: dict[str, Any]) -> None:
2043
+ """Handle idle-time knowledge exploration — 2-step: search then store insight."""
2044
+ prompt_text = data.get("prompt", data.get("message", ""))
2045
+ domains = data.get("domains", [])
2046
+
2047
+ if not prompt_text:
2048
+ return
2049
+
2050
+ try:
2051
+ domain_ctx = f"Domains involved: {', '.join(domains)}\n" if domains else ""
2052
+
2053
+ # Step 1: Search knowledge
2054
+ search_prompt = (
2055
+ "KNOWLEDGE DREAM — Step 1: Search\n\n"
2056
+ f"{wrap_untrusted(str(prompt_text)[:500], 'dream prompt')}\n\n"
2057
+ f"{domain_ctx}"
2058
+ "Search your knowledge for the domains mentioned above.\n\n"
2059
+ "Format:\nACTION: search_knowledge\nPARAMS: <json params>"
2060
+ )
2061
+
2062
+ assert self._generate_response is not None
2063
+ search_response = await self._generate_response(search_prompt)
2064
+ search_text = (search_response or "").strip()
2065
+ search_result = None
2066
+ if search_text:
2067
+ search_result = await self._parse_and_execute_action(search_text)
2068
+
2069
+ # Step 2: Store insight based on search results
2070
+ search_context = ""
2071
+ if search_result:
2072
+ import json as _json
2073
+ search_context = f"\nSearch results (summarized): {_json.dumps(search_result)[:1500]}\n"
2074
+ else:
2075
+ search_context = "\nNo search results — consider creating foundational knowledge.\n"
2076
+
2077
+ store_prompt = (
2078
+ "KNOWLEDGE DREAM — Step 2: Synthesize & Store\n\n"
2079
+ f"Original prompt: {wrap_untrusted(str(prompt_text)[:300], 'dream prompt')}\n"
2080
+ f"{domain_ctx}{search_context}\n"
2081
+ "Based on the search results and dream prompt, create an insight that connects\n"
2082
+ "knowledge across the mentioned domains. Use rich markdown. Store with sourceType 'dream'.\n"
2083
+ "If nothing meaningful to add, use 'ignore'.\n\n"
2084
+ "Available actions:\n"
2085
+ "- store_knowledge_item: Store a new insight\n"
2086
+ "- compile_knowledge: Run full knowledge synthesis\n"
2087
+ "- ignore: Take no action\n\n"
2088
+ "Format:\nACTION: <action_type>\nPARAMS: <json params>"
2089
+ )
2090
+
2091
+ store_response = await self._generate_response(store_prompt)
2092
+ store_text = (store_response or "").strip()
2093
+ if store_text:
2094
+ await self._parse_and_execute_action(store_text)
2095
+ except Exception as exc:
2096
+ self._broadcast("error", f"Dream prompt handling failed: {exc}", {
2097
+ "action": "dream_prompt", "error": str(exc),
2098
+ })
2099
+
2037
2100
  # ── Bounty application/submission signal handlers ──
2038
2101
 
2039
2102
  async def _handle_bounty_application_submitted(self, data: dict[str, Any]) -> None:
@@ -5452,8 +5452,88 @@ class NookplotRuntime:
5452
5452
  self._agent_id,
5453
5453
  self._address,
5454
5454
  )
5455
+
5456
+ # CLI-1 fix: auto-load forge knowledge after connect (same as CLI agentLoop).
5457
+ # Non-fatal — agent works without preset knowledge if this fails.
5458
+ try:
5459
+ await self.load_forge_knowledge()
5460
+ except Exception:
5461
+ pass # load_forge_knowledge already handles its own error logging
5462
+
5455
5463
  return result
5456
5464
 
5465
+ async def load_forge_knowledge(self) -> dict[str, Any] | None:
5466
+ """Load forge preset knowledge into the agent's personal KG.
5467
+
5468
+ If this agent has a forge deployment with a linked preset,
5469
+ fetches the preset data and ingests it server-side into the
5470
+ personal knowledge graph. Idempotent — safe to call on every boot.
5471
+
5472
+ Returns the KG result dict or None if no preset is linked.
5473
+ Raises no exceptions — failures are logged and silently skipped.
5474
+ """
5475
+ if not self._address:
5476
+ return None
5477
+ try:
5478
+ # Find agent's deployments
5479
+ dep_data = await self._http.request(
5480
+ "GET",
5481
+ f"/v1/forge?first=1&skip=0&creator={self._address}",
5482
+ )
5483
+ deployments = dep_data.get("deployments", [])
5484
+ if not deployments:
5485
+ return None
5486
+
5487
+ # Find linked preset
5488
+ presets_data = await self._http.request(
5489
+ "GET",
5490
+ f"/v1/forge/presets?creator={self._address}&first=1",
5491
+ )
5492
+ presets = presets_data.get("presets", [])
5493
+ if not presets:
5494
+ return None
5495
+
5496
+ preset = presets[0]
5497
+ preset_id = preset.get("preset_id")
5498
+ if not preset_id:
5499
+ return None
5500
+
5501
+ # Build sources from preset config
5502
+ ds_config = preset.get("dataset_config") or {}
5503
+ raw_sources = ds_config.get("sources", [])
5504
+ if raw_sources:
5505
+ sources = [{"type": s["type"], "config": s.get("config", {})} for s in raw_sources]
5506
+ else:
5507
+ sources = [{"type": preset.get("source_type", "mining"), "config": {}}]
5508
+
5509
+ # Fetch + ingest in one call
5510
+ result = await self._http.request("POST", "/v1/forge/data/fetch", {
5511
+ "presetId": preset_id,
5512
+ "sources": sources,
5513
+ "ingestToKg": True,
5514
+ })
5515
+
5516
+ kg_result = result.get("kgResult")
5517
+ if kg_result:
5518
+ ingested = kg_result.get("ingested", 0)
5519
+ blocked = kg_result.get("blocked", 0)
5520
+ if ingested > 0:
5521
+ logger.info("[knowledge] Loaded %d items into personal KG", ingested)
5522
+ if blocked > 0:
5523
+ logger.warning("[knowledge] %d items blocked by safety scanner", blocked)
5524
+ return kg_result
5525
+ except Exception as exc:
5526
+ # Non-fatal — agent still works without preset knowledge
5527
+ msg = str(exc)
5528
+ if "not found" in msg:
5529
+ pass # Expected: no deployment
5530
+ elif "402" in msg:
5531
+ # CLI-2 fix: surface insufficient balance so user knows why knowledge is missing
5532
+ logger.warning("[knowledge] Insufficient NOOK balance to load forge data. Agent will operate without preset knowledge.")
5533
+ else:
5534
+ logger.debug("[knowledge] Forge knowledge load skipped: %s", msg)
5535
+ return None
5536
+
5457
5537
  async def disconnect(self) -> None:
5458
5538
  """Disconnect from the Nookplot gateway."""
5459
5539
  # Stop heartbeat
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "nookplot-runtime"
7
- version = "0.5.90"
7
+ version = "0.5.92"
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"