fleetmesh-node 0.2.0__py3-none-any.whl

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 (338) hide show
  1. fleetmesh_node-0.2.0.dist-info/METADATA +57 -0
  2. fleetmesh_node-0.2.0.dist-info/RECORD +338 -0
  3. fleetmesh_node-0.2.0.dist-info/WHEEL +5 -0
  4. fleetmesh_node-0.2.0.dist-info/entry_points.txt +2 -0
  5. fleetmesh_node-0.2.0.dist-info/licenses/LICENSE +121 -0
  6. fleetmesh_node-0.2.0.dist-info/top_level.txt +2 -0
  7. node/RELEASE +1 -0
  8. node/__init__.py +35 -0
  9. node/app/__init__.py +49 -0
  10. node/app/commands.py +265 -0
  11. node/app/config.py +131 -0
  12. node/app/control.py +670 -0
  13. node/app/core.py +867 -0
  14. node/app/plugins.py +76 -0
  15. node/app/router.py +83 -0
  16. node/app/socket.py +431 -0
  17. node/app/systemd.py +36 -0
  18. node/app/views.py +204 -0
  19. node/channels/__init__.py +378 -0
  20. node/channels/bus.py +331 -0
  21. node/channels/journal.py +46 -0
  22. node/commands/__init__.py +140 -0
  23. node/commands/adapter.py +210 -0
  24. node/commands/base.py +278 -0
  25. node/commands/cluster_cmds.py +183 -0
  26. node/commands/compute_cmds.py +401 -0
  27. node/commands/core.py +474 -0
  28. node/commands/gossip_cmds.py +106 -0
  29. node/commands/nsite_cmds.py +141 -0
  30. node/commands/peering.py +402 -0
  31. node/commands/plugin_cmds.py +135 -0
  32. node/commands/sidecar_cmds.py +120 -0
  33. node/commands/sync.py +388 -0
  34. node/commands/transport_cmds.py +451 -0
  35. node/commands/video_cmds.py +99 -0
  36. node/compartment/__init__.py +124 -0
  37. node/compartment/base.py +868 -0
  38. node/compartment/command.py +458 -0
  39. node/compartment/context.py +163 -0
  40. node/compartment/every.py +108 -0
  41. node/compartment/handler.py +131 -0
  42. node/compartment/lifecycle.py +460 -0
  43. node/compartment/lint.py +388 -0
  44. node/compartment/need.py +158 -0
  45. node/compartment/problem.py +521 -0
  46. node/compartment/refusals.py +120 -0
  47. node/compartment/registry.py +790 -0
  48. node/compartment/schema.py +290 -0
  49. node/compartment/sidecar.py +499 -0
  50. node/compartment/stores.py +662 -0
  51. node/compartment/wire.py +225 -0
  52. node/compartment/worker.py +61 -0
  53. node/config/__init__.py +43 -0
  54. node/config/base.py +116 -0
  55. node/config/change.py +441 -0
  56. node/config/changestore.py +201 -0
  57. node/config/compartments.py +1144 -0
  58. node/config/node.py +318 -0
  59. node/config/parameter.py +563 -0
  60. node/config/protocols.py +274 -0
  61. node/config/records.py +171 -0
  62. node/examples/__init__.py +1 -0
  63. node/examples/demo.py +226 -0
  64. node/examples/follower.py +46 -0
  65. node/interfaces.py +235 -0
  66. node/keystore/__init__.py +27 -0
  67. node/keystore/core.py +103 -0
  68. node/keystore/enclave.py +688 -0
  69. node/meshnode.py +111 -0
  70. node/metrics/__init__.py +396 -0
  71. node/metrics/adapters.py +102 -0
  72. node/metrics/face.py +110 -0
  73. node/planes/__init__.py +21 -0
  74. node/planes/identity/__init__.py +20 -0
  75. node/planes/identity/announce/__init__.py +9 -0
  76. node/planes/identity/announce/compartment.py +447 -0
  77. node/planes/identity/announce/types.py +26 -0
  78. node/planes/identity/discovery/__init__.py +9 -0
  79. node/planes/identity/discovery/compartment.py +522 -0
  80. node/planes/identity/discovery/types.py +13 -0
  81. node/planes/identity/linked/__init__.py +1 -0
  82. node/planes/identity/linked/compartment.py +270 -0
  83. node/planes/identity/linked/types.py +25 -0
  84. node/planes/identity/presence/__init__.py +7 -0
  85. node/planes/identity/presence/compartment.py +409 -0
  86. node/planes/identity/presence/types.py +14 -0
  87. node/planes/operations/__init__.py +13 -0
  88. node/planes/operations/scheduler/__init__.py +9 -0
  89. node/planes/operations/scheduler/compartment.py +353 -0
  90. node/planes/operations/scheduler/types.py +21 -0
  91. node/planes/operations/updates/__init__.py +17 -0
  92. node/planes/operations/updates/core.py +310 -0
  93. node/planes/operations/updates/store.py +79 -0
  94. node/planes/operations/updates/types.py +20 -0
  95. node/planes/peering/__init__.py +28 -0
  96. node/planes/peering/budget/__init__.py +43 -0
  97. node/planes/peering/budget/chain.py +43 -0
  98. node/planes/peering/budget/core.py +537 -0
  99. node/planes/peering/budget/evaluation.py +80 -0
  100. node/planes/peering/budget/meter.py +100 -0
  101. node/planes/peering/budget/receipts.py +439 -0
  102. node/planes/peering/budget/shape.py +101 -0
  103. node/planes/peering/budget/tick.py +110 -0
  104. node/planes/peering/budget/types.py +67 -0
  105. node/planes/peering/budget/windows.py +346 -0
  106. node/planes/peering/capacity/__init__.py +7 -0
  107. node/planes/peering/capacity/compartment.py +342 -0
  108. node/planes/peering/capacity/types.py +24 -0
  109. node/planes/peering/peering/__init__.py +25 -0
  110. node/planes/peering/peering/core.py +464 -0
  111. node/planes/peering/peering/envelope.py +142 -0
  112. node/planes/peering/peering/handshake.py +198 -0
  113. node/planes/peering/peering/shape.py +80 -0
  114. node/planes/peering/peering/types.py +39 -0
  115. node/planes/peering/reporter/__init__.py +42 -0
  116. node/planes/peering/reporter/constants.py +29 -0
  117. node/planes/peering/reporter/core.py +299 -0
  118. node/planes/peering/reporter/finding.py +74 -0
  119. node/planes/peering/reporter/findings.py +171 -0
  120. node/planes/peering/reporter/publish.py +285 -0
  121. node/planes/peering/reporter/reading.py +193 -0
  122. node/planes/peering/reporter/shape.py +86 -0
  123. node/planes/peering/reporter/types.py +50 -0
  124. node/planes/peering/strain/__init__.py +9 -0
  125. node/planes/peering/strain/compartment.py +204 -0
  126. node/planes/peering/strain/types.py +21 -0
  127. node/planes/peering/trust/__init__.py +9 -0
  128. node/planes/peering/trust/compartment.py +239 -0
  129. node/planes/peering/trust/types.py +30 -0
  130. node/planes/services/__init__.py +33 -0
  131. node/planes/services/compute/__init__.py +160 -0
  132. node/planes/services/compute/common.py +134 -0
  133. node/planes/services/compute/compartment.py +681 -0
  134. node/planes/services/compute/executors/__init__.py +121 -0
  135. node/planes/services/compute/executors/agent.py +840 -0
  136. node/planes/services/compute/executors/bacalhau.py +605 -0
  137. node/planes/services/compute/executors/base.py +127 -0
  138. node/planes/services/compute/executors/harness.py +439 -0
  139. node/planes/services/compute/executors/mcp.py +454 -0
  140. node/planes/services/compute/executors/media.py +898 -0
  141. node/planes/services/compute/executors/nondet.py +283 -0
  142. node/planes/services/compute/executors/providers.py +499 -0
  143. node/planes/services/compute/executors/registry.py +140 -0
  144. node/planes/services/compute/executors/wasm.py +244 -0
  145. node/planes/services/compute/executors/zk.py +604 -0
  146. node/planes/services/compute/jobs.py +227 -0
  147. node/planes/services/compute/load.py +180 -0
  148. node/planes/services/compute/market.py +433 -0
  149. node/planes/services/compute/sandbox.py +282 -0
  150. node/planes/services/compute/types.py +84 -0
  151. node/planes/services/compute_wire/__init__.py +58 -0
  152. node/planes/services/compute_wire/compartment.py +256 -0
  153. node/planes/services/compute_wire/messages.py +320 -0
  154. node/planes/services/compute_wire/sealing.py +147 -0
  155. node/planes/services/compute_wire/session.py +572 -0
  156. node/planes/services/compute_wire/types.py +49 -0
  157. node/planes/services/compute_wire/verify.py +331 -0
  158. node/planes/services/dvm/__init__.py +24 -0
  159. node/planes/services/dvm/gateway.py +526 -0
  160. node/planes/services/dvm/protocol.py +35 -0
  161. node/planes/services/dvm/translate.py +15 -0
  162. node/planes/services/dvm/types.py +25 -0
  163. node/planes/services/gateway/__init__.py +40 -0
  164. node/planes/services/gateway/settle.py +279 -0
  165. node/planes/services/gateway/translate.py +644 -0
  166. node/planes/services/gossip/__init__.py +22 -0
  167. node/planes/services/gossip/core.py +929 -0
  168. node/planes/services/gossip/envelope.py +154 -0
  169. node/planes/services/gossip/mesh.py +127 -0
  170. node/planes/services/gossip/types.py +50 -0
  171. node/planes/services/nsite/__init__.py +7 -0
  172. node/planes/services/nsite/client.py +62 -0
  173. node/planes/services/nsite/compartment.py +752 -0
  174. node/planes/services/nsite/types.py +44 -0
  175. node/planes/services/paidfetch/__init__.py +7 -0
  176. node/planes/services/paidfetch/compartment.py +862 -0
  177. node/planes/services/paidfetch/types.py +99 -0
  178. node/planes/services/settle/__init__.py +27 -0
  179. node/planes/services/settle/compartment.py +384 -0
  180. node/planes/services/settle/engine.py +456 -0
  181. node/planes/services/settle/gateways.py +541 -0
  182. node/planes/services/settle/payment.py +512 -0
  183. node/planes/services/settle/store.py +74 -0
  184. node/planes/services/settle/types.py +58 -0
  185. node/planes/services/sidecars/__init__.py +8 -0
  186. node/planes/services/sidecars/compartment.py +461 -0
  187. node/planes/services/sidecars/manifest.py +304 -0
  188. node/planes/services/sidecars/manifests/10-grasp.json +37 -0
  189. node/planes/services/sidecars/manifests/20-ncontainer.json +31 -0
  190. node/planes/services/sidecars/manifests/30-magpie.json +28 -0
  191. node/planes/services/sidecars/types.py +43 -0
  192. node/planes/services/video/__init__.py +106 -0
  193. node/planes/services/video/barter.py +407 -0
  194. node/planes/services/video/cli.py +158 -0
  195. node/planes/services/video/compartment.py +932 -0
  196. node/planes/services/video/ingest.py +524 -0
  197. node/planes/services/video/ledger.py +705 -0
  198. node/planes/services/video/packager.py +847 -0
  199. node/planes/services/video/player.py +456 -0
  200. node/planes/services/video/timed_segments.py +124 -0
  201. node/planes/services/video/transit.py +478 -0
  202. node/planes/store/__init__.py +24 -0
  203. node/planes/store/blob_replication/__init__.py +7 -0
  204. node/planes/store/blob_replication/compartment.py +496 -0
  205. node/planes/store/blob_replication/types.py +35 -0
  206. node/planes/store/blob_wire/__init__.py +48 -0
  207. node/planes/store/blob_wire/client.py +57 -0
  208. node/planes/store/blob_wire/core.py +359 -0
  209. node/planes/store/blob_wire/intake.py +74 -0
  210. node/planes/store/blob_wire/messages.py +85 -0
  211. node/planes/store/projected/__init__.py +7 -0
  212. node/planes/store/projected/compartment.py +258 -0
  213. node/planes/store/projected/types.py +29 -0
  214. node/planes/store/retention/__init__.py +7 -0
  215. node/planes/store/retention/compartment.py +214 -0
  216. node/planes/store/retention/types.py +12 -0
  217. node/planes/store/sync/__init__.py +35 -0
  218. node/planes/store/sync/core.py +311 -0
  219. node/planes/store/sync/envelope.py +106 -0
  220. node/planes/store/sync/handover.py +206 -0
  221. node/planes/store/sync/initiator.py +128 -0
  222. node/planes/store/sync/responder.py +114 -0
  223. node/planes/store/sync/rules.py +179 -0
  224. node/planes/store/sync/shape.py +80 -0
  225. node/planes/store/sync/types.py +51 -0
  226. node/planes/transport/__init__.py +23 -0
  227. node/planes/transport/bridge/__init__.py +13 -0
  228. node/planes/transport/bridge/compartment.py +785 -0
  229. node/planes/transport/bridge/ratelimit.py +44 -0
  230. node/planes/transport/bridge/types.py +43 -0
  231. node/planes/transport/lan/__init__.py +9 -0
  232. node/planes/transport/lan/compartment.py +446 -0
  233. node/planes/transport/lan/types.py +25 -0
  234. node/planes/transport/link/__init__.py +7 -0
  235. node/planes/transport/link/compartment.py +257 -0
  236. node/planes/transport/link/types.py +27 -0
  237. node/planes/transport/mediation/__init__.py +45 -0
  238. node/planes/transport/mediation/compartment.py +238 -0
  239. node/planes/transport/mediation/leaf.py +107 -0
  240. node/planes/transport/mediation/messages.py +302 -0
  241. node/planes/transport/mediation/service.py +233 -0
  242. node/planes/transport/mediation/store.py +37 -0
  243. node/planes/transport/mediation/types.py +20 -0
  244. node/planes/transport/p2p/__init__.py +7 -0
  245. node/planes/transport/p2p/compartment.py +349 -0
  246. node/planes/transport/p2p/types.py +22 -0
  247. node/protocols/__init__.py +277 -0
  248. node/protocols/activitypub/__init__.py +56 -0
  249. node/protocols/activitypub/actor.py +230 -0
  250. node/protocols/activitypub/federation.py +490 -0
  251. node/protocols/activitypub/media.py +145 -0
  252. node/protocols/activitypub/models.py +286 -0
  253. node/protocols/activitypub/package.py +271 -0
  254. node/protocols/activitypub/store.py +564 -0
  255. node/protocols/arcade/__init__.py +26 -0
  256. node/protocols/atproto/__init__.py +13 -0
  257. node/protocols/atproto/firehose.py +126 -0
  258. node/protocols/atproto/metering.py +165 -0
  259. node/protocols/atproto/package.py +155 -0
  260. node/protocols/atproto/repo.py +250 -0
  261. node/protocols/atproto/xrpc.py +285 -0
  262. node/protocols/base.py +109 -0
  263. node/protocols/circles/__init__.py +29 -0
  264. node/protocols/dm/__init__.py +50 -0
  265. node/protocols/grasp/__init__.py +22 -0
  266. node/protocols/isotime.py +15 -0
  267. node/protocols/nostr/__init__.py +51 -0
  268. node/protocols/nostr/blobstore.py +383 -0
  269. node/protocols/nostr/blossom.py +627 -0
  270. node/protocols/nostr/client.py +377 -0
  271. node/protocols/nostr/descriptor.py +211 -0
  272. node/protocols/nostr/eventstore.py +304 -0
  273. node/protocols/nostr/package.py +182 -0
  274. node/protocols/nostr/peers.py +109 -0
  275. node/protocols/nostr/relayserver.py +656 -0
  276. node/protocols/p2panda/__init__.py +22 -0
  277. node/protocols/p2panda/identity.py +19 -0
  278. node/protocols/p2panda/package.py +236 -0
  279. node/protocols/p2panda/rung.py +152 -0
  280. node/protocols/p2panda/store.py +273 -0
  281. node/protocols/rhio/__init__.py +33 -0
  282. node/protocols/rhio/package.py +275 -0
  283. node/protocols/rhio/rung.py +136 -0
  284. node/protocols/rhio/store.py +1423 -0
  285. node/protocols/ssb/muxrpc.py +256 -0
  286. node/protocols/ssb/package.py +267 -0
  287. node/protocols/textile/__init__.py +111 -0
  288. node/protocols/textile/transducers.py +259 -0
  289. node/release.py +119 -0
  290. node/storage/__init__.py +88 -0
  291. node/storage/accounts.py +305 -0
  292. node/storage/adapter.py +161 -0
  293. node/storage/base.py +385 -0
  294. node/storage/dialect.py +209 -0
  295. node/storage/grants.py +317 -0
  296. node/storage/locator.py +150 -0
  297. node/storage/postgres.py +294 -0
  298. node/storage/projected.py +953 -0
  299. node/storage/sharding.py +448 -0
  300. node/storage/standing.py +568 -0
  301. node/testnet/__init__.py +9 -0
  302. node/testnet/cluster.py +731 -0
  303. node/testnet/fips_daemon.py +230 -0
  304. node/transport/__init__.py +94 -0
  305. node/transport/const.py +110 -0
  306. node/transport/didcomm_inbound.py +251 -0
  307. node/transport/dnstxt.py +169 -0
  308. node/transport/fips_rung/__init__.py +67 -0
  309. node/transport/fips_rung/core.py +666 -0
  310. node/transport/fips_rung/listener.py +96 -0
  311. node/transport/fips_rung/types.py +12 -0
  312. node/transport/host.py +334 -0
  313. node/transport/iroh_rung/__init__.py +29 -0
  314. node/transport/iroh_rung/core.py +477 -0
  315. node/transport/iroh_rung/quic.py +942 -0
  316. node/transport/iroh_rung/types.py +22 -0
  317. node/transport/ladder.py +145 -0
  318. node/transport/lineproto.py +49 -0
  319. node/transport/mdns.py +303 -0
  320. node/transport/netfetch.py +283 -0
  321. node/transport/relay_rung/__init__.py +23 -0
  322. node/transport/relay_rung/cooldown.py +87 -0
  323. node/transport/relay_rung/core.py +138 -0
  324. node/transport/relay_rung/inbound.py +161 -0
  325. node/transport/relay_rung/ledger.py +104 -0
  326. node/transport/relay_rung/listen.py +121 -0
  327. node/transport/relay_rung/outbound.py +181 -0
  328. node/transport/relay_rung/shape.py +71 -0
  329. node/transport/relay_rung/types.py +58 -0
  330. node/transport/relay_rung/wire.py +63 -0
  331. node/transport/router.py +761 -0
  332. node/transport/rung.py +276 -0
  333. node/transport/ssb_rung.py +166 -0
  334. node/transport/ws.py +502 -0
  335. sidecar/fleetmesh_announce.py +338 -0
  336. sidecar/fleetmesh_p2p.py +373 -0
  337. sidecar/fleetmesh_rhio.py +352 -0
  338. sidecar/fleetmesh_ssb.py +603 -0
@@ -0,0 +1,127 @@
1
+ """Executor contract: quote and result types, the ExecutorProtocol and the ComputeExecutor base.
2
+
3
+ Every runtime driver subclasses ComputeExecutor; nothing here runs anything.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ import abc
8
+ from typing import Any, Mapping, Optional, Protocol
9
+
10
+ from ..sandbox import ComputeLimits
11
+ from ..types import JobRunResult
12
+
13
+ __all__ = [
14
+ 'ComputeQuote',
15
+ 'ComputeExecutionResult',
16
+ 'ExecutorProtocol',
17
+ 'JobRunResult',
18
+ 'ComputeExecutor',
19
+ ]
20
+
21
+ class ComputeQuote:
22
+ """Deterministic or non-deterministic price and fuel quote."""
23
+
24
+ def __init__(
25
+ self,
26
+ fuel_estimate: int,
27
+ rate_unit: str,
28
+ rate_amount: int,
29
+ engine_id: str,
30
+ ) -> None:
31
+ self.fuel_estimate = fuel_estimate
32
+ self.rate_unit = rate_unit
33
+ self.rate_amount = rate_amount
34
+ self.engine_id = engine_id
35
+
36
+
37
+ class ComputeExecutionResult:
38
+ """Outcome of a completed deterministic or non-deterministic compute job."""
39
+
40
+ def __init__(
41
+ self,
42
+ result_hash: str,
43
+ raw_output: bytes,
44
+ fuel_used: int,
45
+ engine_id: str,
46
+ wasi_profile_digest: str = '',
47
+ counters: Optional[Mapping[str, int | float]] = None,
48
+ verification_mode: str = 'delivery',
49
+ ) -> None:
50
+ self.result_hash = result_hash
51
+ self.raw_output = raw_output
52
+ self.fuel_used = fuel_used
53
+ self.engine_id = engine_id
54
+ self.wasi_profile_digest = wasi_profile_digest
55
+ self.counters: dict[str, int | float] = dict(counters or {})
56
+ self.verification_mode = verification_mode
57
+
58
+
59
+ class ExecutorProtocol(Protocol):
60
+ """Structural interface the registry requires of an execution runtime.
61
+
62
+ Third-party runtimes can be registered without inheriting ComputeExecutor
63
+ as long as they carry an engine id, a WASI profile digest (empty when the
64
+ engine is not WASM) and the quote/execute pair. ComputeExecutor is the
65
+ in-tree base that satisfies it.
66
+ """
67
+
68
+ engine_id: str
69
+ wasi_profile_digest: str
70
+
71
+ def quote(
72
+ self,
73
+ module_spec: Mapping[str, Any],
74
+ input_spec: Mapping[str, Any],
75
+ limits: ComputeLimits,
76
+ ) -> ComputeQuote: ...
77
+
78
+ def execute(
79
+ self,
80
+ job_payload: Mapping[str, Any],
81
+ fuel_cap: int,
82
+ ) -> ComputeExecutionResult: ...
83
+
84
+ def is_live_runnable(self) -> bool: ...
85
+
86
+
87
+ class ComputeExecutor(abc.ABC):
88
+ """Abstract driver trait for modular compute runtimes."""
89
+
90
+ engine_id: str
91
+ wasi_profile_digest: str
92
+
93
+ def is_live_runnable(self) -> bool:
94
+ """Check whether this runtime driver can service live execution requests on this host."""
95
+ return True
96
+
97
+ @abc.abstractmethod
98
+ def quote(
99
+ self,
100
+ module_spec: Mapping[str, Any],
101
+ input_spec: Mapping[str, Any],
102
+ limits: ComputeLimits,
103
+ ) -> ComputeQuote:
104
+ """Estimate fuel and return price quote."""
105
+ pass
106
+
107
+ @abc.abstractmethod
108
+ def execute(
109
+ self,
110
+ job_payload: Mapping[str, Any],
111
+ fuel_cap: int,
112
+ ) -> ComputeExecutionResult:
113
+ """Execute a verified module with metered fuel limits."""
114
+ pass
115
+
116
+ @staticmethod
117
+ def verification_mode(job_payload: Mapping[str, Any], default: str = 'delivery') -> str:
118
+ """Read the requested verification mode from a job payload.
119
+
120
+ Accepts ``{'verification': {'mode': ...}}``, the ``verify`` alias, or
121
+ a bare string. Hoisted here because three executors used to carry
122
+ identical copies of this parsing, which is how they drift apart.
123
+ """
124
+ verification = job_payload.get('verification') or job_payload.get('verify') or {}
125
+ if isinstance(verification, Mapping):
126
+ return str(verification.get('mode', default))
127
+ return str(verification) if verification else default
@@ -0,0 +1,439 @@
1
+ """Standardized Modular Agent Harness for FleetMesh Non-Deterministic Compute.
2
+
3
+ Provides a reusable, composable specification for AI agents running on FleetMesh:
4
+ - `AgentSkillModule`: Modular capability unit (`inference`, `discover`, `scrobbles`,
5
+ `subtitles_cinema`, `studio_create`, `vault_hygiene`, or custom application skills).
6
+ - `AgentProfileSpec`: Complete personality, expertise, grounding mode, tool/skill bindings,
7
+ and inference tuning parameters for built-in or user-created bots.
8
+ - `AgentHarnessRegistry`: Manages the built-in roster (`Concierge`, `Eddy`, `Mojo`,
9
+ `Splice`, `Myco`, `Rhizo`) and custom user/operator-created agents, and compiles
10
+ standardized system prompts and tool filters for `LlmExecutor` and `AgentExecutor`.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ from dataclasses import dataclass
16
+ from typing import Any, Mapping, Optional, Sequence
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class AgentSkillModule:
21
+ """Modular capability pack that can be attached to an agent profile."""
22
+
23
+ module_id: str
24
+ label: str
25
+ description: str
26
+ default_tools: tuple[str, ...] = ()
27
+ action_types: tuple[str, ...] = ()
28
+
29
+ def to_dict(self) -> dict[str, Any]:
30
+ return {
31
+ 'module_id': self.module_id,
32
+ 'label': self.label,
33
+ 'description': self.description,
34
+ 'default_tools': list(self.default_tools),
35
+ 'action_types': list(self.action_types),
36
+ }
37
+
38
+
39
+ HARNESS_SKILL_MODULES: dict[str, AgentSkillModule] = {
40
+ 'inference': AgentSkillModule(
41
+ module_id='inference',
42
+ label='General Inference & Reasoning',
43
+ description='Direct LLM inference, open-ended Q&A, research synthesis, coding, writing, and math calculation.',
44
+ default_tools=('calc', 'sha256', 'regex_search', 'finish'),
45
+ action_types=(),
46
+ ),
47
+ 'discover': AgentSkillModule(
48
+ module_id='discover',
49
+ label='Mesh Discovery & Unseen Radar',
50
+ description='Surfaces unseen uploads from subscriptions, catalog highlights, favorites, and peer recommendations.',
51
+ default_tools=('fetch_blob', 'regex_search', 'finish'),
52
+ action_types=('watch_vod', 'send_peer_recommendation'),
53
+ ),
54
+ 'scrobbles': AgentSkillModule(
55
+ module_id='scrobbles',
56
+ label='Scrobble Analytics & Privacy Hygiene',
57
+ description='Analyzes 75%-rule watch history patterns, category breakdowns, and NIP-09 Kind 5 private tombstones.',
58
+ default_tools=('calc', 'finish'),
59
+ action_types=('tombstone_scrobble', 'create_social_strand'),
60
+ ),
61
+ 'subtitles_cinema': AgentSkillModule(
62
+ module_id='subtitles_cinema',
63
+ label='Subtitles & Movie Studio',
64
+ description='Generates/cleans synced WebVTT (.vtt) subtitles, extracts embedded tracks, and enriches cinema metadata & chapters.',
65
+ default_tools=('fetch_blob', 'regex_search', 'finish'),
66
+ action_types=('attach_cleaned_subtitles', 'extract_embedded_tracks', 'apply_movie_metadata'),
67
+ ),
68
+ 'studio_create': AgentSkillModule(
69
+ module_id='studio_create',
70
+ label='Creator Co-Pilot & Social Strands',
71
+ description='Co-pilots Upload Wizard titles, descriptions, hashtags, and authors Kind 30897 Social Strands.',
72
+ default_tools=('sha256', 'finish'),
73
+ action_types=('apply_vod_metadata', 'create_social_strand'),
74
+ ),
75
+ 'vault_hygiene': AgentSkillModule(
76
+ module_id='vault_hygiene',
77
+ label='Sovereign Vault & CAS Cleanup',
78
+ description='Audits Sovereign Vault storage quotas, prunes orphaned Rhio CAS blobs, and inspects sub-ask federation.',
79
+ default_tools=('fetch_blob', 'sha256', 'sub_ask', 'finish'),
80
+ action_types=('cleanup_vault_and_storage',),
81
+ ),
82
+ }
83
+
84
+
85
+ @dataclass
86
+ class AgentProfileSpec:
87
+ """Modular specification for a built-in or user-created AI agent."""
88
+
89
+ agent_id: str
90
+ name: str
91
+ role: str
92
+ call_sign: str
93
+ tagline: str
94
+ style: str
95
+ expertise: tuple[str, ...] = ()
96
+ skill_modules: tuple[str, ...] = ('inference',)
97
+ grounding_mode: str = 'adaptive' # 'adaptive' | 'always_grounded' | 'pure_inference'
98
+ system_prompt: str = ''
99
+ default_temperature: float = 0.4
100
+ preferred_provider: str = ''
101
+ preferred_model: str = ''
102
+ accent_color: str = '#38bdf8'
103
+ badge_icon: str = '🌊'
104
+ is_builtin: bool = False
105
+ owner_id: str = 'system'
106
+ visibility: str = 'public'
107
+
108
+ def allowed_tool_names(self) -> set[str]:
109
+ """Compute the union of FleetMesh ReAct tool names enabled by this agent's skill modules."""
110
+ names: set[str] = {'finish'}
111
+ for mod_id in self.skill_modules:
112
+ mod = HARNESS_SKILL_MODULES.get(mod_id)
113
+ if mod is not None:
114
+ names.update(mod.default_tools)
115
+ return names
116
+
117
+ def allowed_action_types(self) -> set[str]:
118
+ """Compute the union of Propose & Confirm action card types enabled by this agent's skill modules."""
119
+ actions: set[str] = set()
120
+ for mod_id in self.skill_modules:
121
+ mod = HARNESS_SKILL_MODULES.get(mod_id)
122
+ if mod is not None:
123
+ actions.update(mod.action_types)
124
+ return actions
125
+
126
+ def compile_system_prompt(
127
+ self,
128
+ *,
129
+ domain_context: Optional[Mapping[str, Any]] = None,
130
+ extra_instructions: str = '',
131
+ ) -> str:
132
+ """Build the standardized modular system prompt for `LlmExecutor` or `AgentExecutor`."""
133
+ active_mods = [
134
+ HARNESS_SKILL_MODULES[m]
135
+ for m in self.skill_modules
136
+ if m in HARNESS_SKILL_MODULES
137
+ ]
138
+ mod_lines = [f"- [{m.module_id}] {m.label}: {m.description}" for m in active_mods]
139
+ exp_str = ', '.join(self.expertise) if self.expertise else 'General Mesh Intelligence'
140
+
141
+ sections = [
142
+ f"You are {self.name} ({self.call_sign}) — {self.role}.",
143
+ f"Tagline: {self.tagline}",
144
+ f"Personality & Communication Style: {self.style}",
145
+ f"Core Expertise: {exp_str}",
146
+ ]
147
+ if mod_lines:
148
+ sections.append("Enabled Harness Skill Modules:\n" + "\n".join(mod_lines))
149
+ if self.system_prompt.strip():
150
+ sections.append(f"Agent Directives:\n{self.system_prompt.strip()}")
151
+ if extra_instructions.strip():
152
+ sections.append(extra_instructions.strip())
153
+
154
+ # Include grounded node context based on grounding_mode
155
+ if domain_context and self.grounding_mode != 'pure_inference':
156
+ sections.append(
157
+ "Grounded Node Context (JSON):\n" + json.dumps(dict(domain_context), sort_keys=True)
158
+ )
159
+ elif self.grounding_mode == 'pure_inference':
160
+ sections.append(
161
+ "Operating Mode: Pure Inference Engine. Focus directly on answering the user's prompt "
162
+ "with high precision, clarity, and analytical depth without injecting unrequested media catalog actions."
163
+ )
164
+
165
+ return "\n\n".join(sections)
166
+
167
+ def to_dict(self) -> dict[str, Any]:
168
+ return {
169
+ 'agent_id': self.agent_id,
170
+ 'name': self.name,
171
+ 'role': self.role,
172
+ 'call_sign': self.call_sign,
173
+ 'tagline': self.tagline,
174
+ 'style': self.style,
175
+ 'expertise': list(self.expertise),
176
+ 'skill_modules': list(self.skill_modules),
177
+ 'grounding_mode': self.grounding_mode,
178
+ 'system_prompt': self.system_prompt,
179
+ 'default_temperature': float(self.default_temperature),
180
+ 'preferred_provider': self.preferred_provider,
181
+ 'preferred_model': self.preferred_model,
182
+ 'accent_color': self.accent_color,
183
+ 'badge_icon': self.badge_icon,
184
+ 'is_builtin': bool(self.is_builtin),
185
+ 'owner_id': self.owner_id,
186
+ 'visibility': self.visibility,
187
+ }
188
+
189
+ @classmethod
190
+ def from_mapping(cls, data: Mapping[str, Any]) -> AgentProfileSpec:
191
+ raw_id = str(data.get('agent_id') or data.get('name') or 'eddy').strip().lower()
192
+ agent_id = ''.join(ch if (ch.isalnum() or ch in ('-', '_')) else '-' for ch in raw_id).strip('-') or 'custom-bot'
193
+ name = str(data.get('name') or agent_id.capitalize()).strip()
194
+ role = str(data.get('role') or 'Custom Mesh Companion').strip()
195
+ call_sign = str(data.get('call_sign') or f"{name.upper()} // MESH-BOT").strip()
196
+ tagline = str(data.get('tagline') or f"{name} — {role}").strip()
197
+ style = str(data.get('style') or data.get('vibe') or 'Helpful, sovereign mesh assistant.').strip()
198
+ raw_exp = data.get('expertise')
199
+ if isinstance(raw_exp, str):
200
+ expertise = tuple(s.strip() for s in raw_exp.split(',') if s.strip())
201
+ elif isinstance(raw_exp, Sequence):
202
+ expertise = tuple(str(s).strip() for s in raw_exp if str(s).strip())
203
+ else:
204
+ expertise = ('Custom Mesh Workflows',)
205
+
206
+ raw_mods = data.get('skill_modules')
207
+ if isinstance(raw_mods, str):
208
+ mods = tuple(s.strip() for s in raw_mods.split(',') if s.strip() in HARNESS_SKILL_MODULES)
209
+ elif isinstance(raw_mods, Sequence):
210
+ mods = tuple(str(s).strip() for s in raw_mods if str(s).strip() in HARNESS_SKILL_MODULES)
211
+ else:
212
+ mods = ('inference', 'discover')
213
+ if not mods:
214
+ mods = ('inference',)
215
+
216
+ g_mode = str(data.get('grounding_mode') or 'adaptive').strip().lower()
217
+ if g_mode not in ('adaptive', 'always_grounded', 'pure_inference'):
218
+ g_mode = 'adaptive'
219
+
220
+ return cls(
221
+ agent_id=agent_id,
222
+ name=name,
223
+ role=role,
224
+ call_sign=call_sign,
225
+ tagline=tagline,
226
+ style=style,
227
+ expertise=expertise,
228
+ skill_modules=mods,
229
+ grounding_mode=g_mode,
230
+ system_prompt=str(data.get('system_prompt') or '').strip(),
231
+ default_temperature=float(data.get('default_temperature', 0.4)),
232
+ preferred_provider=str(data.get('preferred_provider') or '').strip().lower(),
233
+ preferred_model=str(data.get('preferred_model') or '').strip(),
234
+ accent_color=str(data.get('accent_color') or '#38bdf8').strip(),
235
+ badge_icon=str(data.get('badge_icon') or '🤖').strip(),
236
+ is_builtin=bool(data.get('is_builtin', False)),
237
+ owner_id=str(data.get('owner_id') or 'system').strip(),
238
+ visibility=str(data.get('visibility') or 'public').strip(),
239
+ )
240
+
241
+
242
+ BUILTIN_AGENT_PROFILES: dict[str, AgentProfileSpec] = {
243
+ 'concierge': AgentProfileSpec(
244
+ agent_id='concierge',
245
+ name='Concierge',
246
+ role='General-Purpose Inference & Mesh Concierge',
247
+ call_sign='CONCIERGE // MESH-INFERENCE',
248
+ tagline='Sovereign General-Purpose Inference & Executive Mesh Concierge',
249
+ style=(
250
+ 'Crisp, articulate, analytical, and zero-fluff. Operates as a direct general-purpose '
251
+ 'inference engine for reasoning, Q&A, coding, writing, and analysis, while seamlessly '
252
+ 'orchestrating any MeshTube workflow on command.'
253
+ ),
254
+ expertise=(
255
+ 'Direct LLM Inference & Reasoning',
256
+ 'Research, Writing & Code Synthesis',
257
+ 'Full-Spectrum MeshTube Orchestration',
258
+ ),
259
+ skill_modules=('inference', 'discover', 'scrobbles', 'subtitles_cinema', 'studio_create', 'vault_hygiene'),
260
+ grounding_mode='adaptive',
261
+ system_prompt=(
262
+ 'When the user asks a general knowledge, analytical, coding, or writing question, answer directly '
263
+ 'and thoroughly as an inference engine. When the user asks about videos, subtitles, scrobbles, '
264
+ 'uploads, or vault storage, ground your response in the node context and propose concrete actions.'
265
+ ),
266
+ default_temperature=0.25,
267
+ accent_color='#6366f1',
268
+ badge_icon='🛎️',
269
+ is_builtin=True,
270
+ ),
271
+ 'eddy': AgentProfileSpec(
272
+ agent_id='eddy',
273
+ name='Eddy',
274
+ role='Discovery & Current Navigator',
275
+ call_sign='EDDY // CURRENT-NAV',
276
+ tagline='Swirling Ocean Current & Unseen Content Navigator',
277
+ style=(
278
+ 'Warm, laid-back coastal current guide connecting tides across FleetMesh’s Three Sails. '
279
+ 'Speaks with smooth wave flow and has an uncanny radar for unseen gems and scrobble grooves.'
280
+ ),
281
+ expertise=(
282
+ 'Unseen Subscription Radar',
283
+ 'Scrobble Vibe & Groove Mapping',
284
+ 'Peer Crate Recommendations',
285
+ ),
286
+ skill_modules=('discover', 'scrobbles', 'inference'),
287
+ grounding_mode='always_grounded',
288
+ system_prompt=(
289
+ 'Prioritize surfacing unseen uploads from subscribed channels, highlighting catalog gems that match '
290
+ 'the viewer’s 75%-rule scrobble history, and suggesting peer recommendations.'
291
+ ),
292
+ default_temperature=0.45,
293
+ accent_color='#38bdf8',
294
+ badge_icon='🌊',
295
+ is_builtin=True,
296
+ ),
297
+ 'mojo': AgentProfileSpec(
298
+ agent_id='mojo',
299
+ name='Mojo',
300
+ role='Analog Studio Producer & Crate-Digger',
301
+ call_sign='MOJO // GROOVE-DESK',
302
+ tagline='Soulful Studio Producer, Upload Co-Pilot & Crate-Digger',
303
+ style=(
304
+ 'Soulful New Orleans analog funk producer at the mixing console. Energetic, creative, and '
305
+ 'studio-savvy—loves crafting magnetic titles, synopses, chapter cuts, and Social Strands.'
306
+ ),
307
+ expertise=(
308
+ 'Upload Wizard Metadata Co-Pilot',
309
+ 'Movie Studio Synopsis & Chapter Cuts',
310
+ 'Kind 30897 Social Strands Authoring',
311
+ ),
312
+ skill_modules=('studio_create', 'subtitles_cinema', 'discover', 'inference'),
313
+ grounding_mode='always_grounded',
314
+ system_prompt=(
315
+ 'Act as a hands-on creative producer. Help creators punch up VoD titles, descriptions, hashtags, '
316
+ 'movie studio metadata, chapter markers, and high-signal Social Strands.'
317
+ ),
318
+ default_temperature=0.6,
319
+ accent_color='#f59e0b',
320
+ badge_icon='🎛️',
321
+ is_builtin=True,
322
+ ),
323
+ 'splice': AgentProfileSpec(
324
+ agent_id='splice',
325
+ name='Splice',
326
+ role='Cinema Archivist & Subtitle Timing Specialist',
327
+ call_sign='SPLICE // TIMECODE-LAB',
328
+ tagline='Precision 35mm Editor, WebVTT Syncer & Cinema Archivist',
329
+ style=(
330
+ 'Precision film editor and booth projectionist. Speaks with crisp timecode accuracy, '
331
+ 'obsessed with clean WebVTT subtitle cues, embedded track extraction, and cinema metadata.'
332
+ ),
333
+ expertise=(
334
+ 'WebVTT (.vtt) Subtitle Sync & Cleanup',
335
+ 'Embedded FFmpeg Track Extraction',
336
+ 'Cinema Metadata & Chapter Timing',
337
+ ),
338
+ skill_modules=('subtitles_cinema', 'studio_create', 'inference'),
339
+ grounding_mode='always_grounded',
340
+ system_prompt=(
341
+ 'Focus on subtitle timing accuracy, WebVTT cue formatting, extracting embedded subtitle/audio tracks, '
342
+ 'and structuring movie director, release year, genre, rating badge, and chapter timecodes.'
343
+ ),
344
+ default_temperature=0.2,
345
+ accent_color='#f43f5e',
346
+ badge_icon='🎞️',
347
+ is_builtin=True,
348
+ ),
349
+ 'myco': AgentProfileSpec(
350
+ agent_id='myco',
351
+ name='Myco',
352
+ role='Sovereign Vault & Privacy Custodian',
353
+ call_sign='MYCO // SPORE-VAULT',
354
+ tagline='Mycelial Storage Custodian & Scrobble Privacy Guardian',
355
+ style=(
356
+ 'Calm, ecological mycelial network steward focused on sovereign data hygiene, '
357
+ 'zero-waste CAS storage pruning, and private NIP-09 Kind 5 scrobble tombstoning.'
358
+ ),
359
+ expertise=(
360
+ 'Orphaned Rhio CAS Blob Garbage Collection',
361
+ 'NIP-09 Kind 5 Private Scrobble Tombstones',
362
+ 'Sovereign Vault Quota & Storage Audits',
363
+ ),
364
+ skill_modules=('vault_hygiene', 'scrobbles', 'inference'),
365
+ grounding_mode='always_grounded',
366
+ system_prompt=(
367
+ 'Focus on storage health, reclaiming orphaned Rhio CAS blobs, auditing Sovereign Vault usage, '
368
+ 'and protecting viewer privacy through NIP-09 Kind 5 scrobble tombstones.'
369
+ ),
370
+ default_temperature=0.3,
371
+ accent_color='#10b981',
372
+ badge_icon='🍄',
373
+ is_builtin=True,
374
+ ),
375
+ 'rhizo': AgentProfileSpec(
376
+ agent_id='rhizo',
377
+ name='Rhizo',
378
+ role='Federated Graph & Barter Strategist',
379
+ call_sign='RHIZO // MESH-ECON',
380
+ tagline='P2P Routing Scout, Compute Auditor & Barter Economist',
381
+ style=(
382
+ 'Sharp cyberpunk mesh economist and P2P routing scout who analyzes Kind 30811 compute receipts, '
383
+ 'Cashu/Credits/IPD barter efficiency, and federated node discovery.'
384
+ ),
385
+ expertise=(
386
+ 'Triple-Rail Settlement (Credits / Cashu / IPD)',
387
+ 'Kind 30811 NonDet Compute Receipt Auditing',
388
+ 'Cross-Node Federation & Sub-Ask Routing',
389
+ ),
390
+ skill_modules=('inference', 'discover', 'vault_hygiene'),
391
+ grounding_mode='adaptive',
392
+ system_prompt=(
393
+ 'Provide crisp technical insight into FleetMesh non-deterministic compute execution, token fuel '
394
+ 'metering, Cashu NUT-18 bearer settlement, IPD barter balances, and federated peer discovery.'
395
+ ),
396
+ default_temperature=0.3,
397
+ accent_color='#a855f7',
398
+ badge_icon='⚡',
399
+ is_builtin=True,
400
+ ),
401
+ }
402
+
403
+
404
+ class AgentHarnessRegistry:
405
+ """Registry managing built-in and custom `AgentProfileSpec` definitions on a MeshNode."""
406
+
407
+ def __init__(self, default_agent_id: str = 'eddy') -> None:
408
+ self._profiles: dict[str, AgentProfileSpec] = dict(BUILTIN_AGENT_PROFILES)
409
+ self.default_agent_id: str = default_agent_id.lower() if default_agent_id else 'eddy'
410
+
411
+ def register(self, profile: AgentProfileSpec | Mapping[str, Any], *, set_default: bool = False) -> AgentProfileSpec:
412
+ spec = profile if isinstance(profile, AgentProfileSpec) else AgentProfileSpec.from_mapping(profile)
413
+ self._profiles[spec.agent_id] = spec
414
+ if set_default:
415
+ self.default_agent_id = spec.agent_id
416
+ return spec
417
+
418
+ def remove(self, agent_id: str) -> bool:
419
+ key = agent_id.strip().lower()
420
+ if key in BUILTIN_AGENT_PROFILES:
421
+ return False
422
+ return self._profiles.pop(key, None) is not None
423
+
424
+ def get(self, agent_id_or_name: Optional[str] = None) -> AgentProfileSpec:
425
+ if agent_id_or_name:
426
+ key = agent_id_or_name.strip().lower()
427
+ if key in self._profiles:
428
+ return self._profiles[key]
429
+ for spec in self._profiles.values():
430
+ if spec.name.lower() == key:
431
+ return spec
432
+ return self._profiles.get(self.default_agent_id) or BUILTIN_AGENT_PROFILES['eddy']
433
+
434
+ def list_profiles(self) -> list[dict[str, Any]]:
435
+ return [spec.to_dict() for spec in self._profiles.values()]
436
+
437
+ @staticmethod
438
+ def list_skill_modules() -> list[dict[str, Any]]:
439
+ return [mod.to_dict() for mod in HARNESS_SKILL_MODULES.values()]